/*-------------------------------------------------------------------------
 *
 * elog.c
 *      error logging and reporting
 *
 * Because of the extremely high rate at which log messages can be generated,
 * we need to be mindful of the performance cost of obtaining any information
 * that may be logged.  Also, it's important to keep in mind that this code may
 * get called from within an aborted transaction, in which case operations
 * such as syscache lookups are unsafe.
 *
 * Some notes about recursion and errors during error processing:
 *
 * We need to be robust about recursive-error scenarios --- for example,
 * if we run out of memory, it's important to be able to report that fact.
 * There are a number of considerations that go into this.
 *
 * First, distinguish between re-entrant use and actual recursion.  It
 * is possible for an error or warning message to be emitted while the
 * parameters for an error message are being computed.  In this case
 * errstart has been called for the outer message, and some field values
 * may have already been saved, but we are not actually recursing.  We handle
 * this by providing a (small) stack of ErrorData records.  The inner message
 * can be computed and sent without disturbing the state of the outer message.
 * (If the inner message is actually an error, this isn't very interesting
 * because control won't come back to the outer message generator ... but
 * if the inner message is only debug or log data, this is critical.)
 *
 * Second, actual recursion will occur if an error is reported by one of
 * the elog.c routines or something they call.  By far the most probable
 * scenario of this sort is "out of memory"; and it's also the nastiest
 * to handle because we'd likely also run out of memory while trying to
 * report this error!  Our escape hatch for this case is to reset the
 * ErrorContext to empty before trying to process the inner error.  Since
 * ErrorContext is guaranteed to have at least 8K of space in it (see mcxt.c),
 * we should be able to process an "out of memory" message successfully.
 * Since we lose the prior error state due to the reset, we won't be able
 * to return to processing the original error, but we wouldn't have anyway.
 * (NOTE: the escape hatch is not used for recursive situations where the
 * inner message is of less than ERROR severity; in that case we just
 * try to process it and return normally.  Usually this will work, but if
 * it ends up in infinite recursion, we will PANIC due to error stack
 * overflow.)
 *
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * This source code file contains modifications made by THL A29 Limited ("Tencent Modifications").
 * All Tencent Modifications are Copyright (C) 2023 THL A29 Limited.
 *
 * IDENTIFICATION
 *      src/backend/utils/error/elog.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif

#include "access/transam.h"
#include "access/xact.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#ifdef PGXC
#include "pgxc/pgxc.h"
#include "pgxc/execRemote.h"
#endif
#ifdef __OPENTENBASE__
#include "executor/nodeAgg.h"
#include "executor/nodeHashjoin.h"
#include "pgxc/squeue.h"
#include "tcop/utility.h"
#include "commands/extension.h"
#include "replication/logicalrelation.h"
extern void PostmasterEnableLogTimeout(void);
extern void PostmasterDisableTimeout(void);
#endif


/* In this module, access gettext() via err_gettext() */
#undef _
#define _(x) err_gettext(x)

#ifdef USE_MODULE_MSGIDS
static void AtProcExit_MsgModule(int code, Datum arg);
static bool pg_msgmodule_enable_disable(int32 pid, bool enable);
#endif

/* Global variables */
ErrorContextCallback *error_context_stack = NULL;

sigjmp_buf *PG_exception_stack = NULL;

extern bool redirection_done;

#ifdef __OPENTENBASE__
extern PGDLLIMPORT int g_in_plpgsql_exec_fun;
#endif

#ifdef __OPENTENBASE__
/* for error code contrib */
ErrcodeHookType g_pfErrcodeHook;
#endif


/*
 * Hook for intercepting messages before they are sent to the server log.
 * Note that the hook will not get called for messages that are suppressed
 * by log_min_messages.  Also note that logging hooks implemented in preload
 * libraries will miss any log messages that are generated before the
 * library is loaded.
 */
emit_log_hook_type emit_log_hook = NULL;

/* GUC parameters */
int            Log_error_verbosity = PGERROR_VERBOSE;
char       *Log_line_prefix = NULL; /* format for extra log line info */
int            Log_destination = LOG_DESTINATION_STDERR;
char       *Log_destination_string = NULL;
bool        syslog_sequence_numbers = true;
bool        syslog_split_messages = true;

#ifdef HAVE_SYSLOG

/*
 * Max string length to send to syslog().  Note that this doesn't count the
 * sequence-number prefix we add, and of course it doesn't count the prefix
 * added by syslog itself.  Solaris and sysklogd truncate the final message
 * at 1024 bytes, so this value leaves 124 bytes for those prefixes.  (Most
 * other syslog implementations seem to have limits of 2KB or so.)
 */
#ifndef PG_SYSLOG_LIMIT
#define PG_SYSLOG_LIMIT 900
#endif

static bool openlog_done = false;
static char *syslog_ident = NULL;
static int    syslog_facility = LOG_LOCAL0;

static void write_syslog(int level, const char *line);
#endif

#ifdef WIN32
extern char *event_source;

static void write_eventlog(int level, const char *line, int len);
#endif

/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE  5

static ErrorData errordata[ERRORDATA_STACK_SIZE];

static int    errordata_stack_depth = -1; /* index of topmost active frame */

static int    recursion_depth = 0;    /* to detect actual recursion */

/*
 * Saved timeval and buffers for formatted timestamps that might be used by
 * both log_line_prefix and csv logs.
 */
static struct timeval saved_timeval;
static bool saved_timeval_set = false;

#define FORMATTED_TS_LEN 128
static char formatted_start_time[FORMATTED_TS_LEN];
static char formatted_log_time[FORMATTED_TS_LEN];


/* Macro for checking errordata_stack_depth is reasonable */
#define CHECK_STACK_DEPTH() \
    do { \
        if (errordata_stack_depth < 0) \
        { \
            errordata_stack_depth = -1; \
            ereport(ERROR, (errmsg_internal("errstart was not called"))); \
        } \
    } while (0)


static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
static void write_console(const char *line, int len);
static void setup_formatted_log_time(void);
static void setup_formatted_start_time(void);
static const char *process_log_prefix_padding(const char *p, int *padding);
static void log_line_prefix(StringInfo buf, ErrorData *edata);
static void write_csvlog(ErrorData *edata);
static void send_message_to_server_log(ErrorData *edata);
static void write_pipe_chunks(char *data, int len, int dest);
static void send_message_to_frontend(ErrorData *edata);
static char *expand_fmt_string(const char *fmt, ErrorData *edata);
static const char *useful_strerror(int errnum);
static const char *get_errno_symbol(int errnum);
static const char *error_severity(int elevel);
static void append_with_tabs(StringInfo buf, const char *str);
static bool is_log_level_output(int elevel,
#ifdef USE_MODULE_MSGIDS
        int moduleid,
        int fileid,
        int msgid,
#endif
        int log_min_level);

#ifdef USE_MODULE_MSGIDS
typedef struct MsgModuleCtlStruct
{
    bool    mm_enabled;
    bool    mm_persistent;
    char    mm_flags[FLEXIBLE_ARRAY_MEMBER];
} MsgModuleCtlStruct;

#define StartOfBackendFlags     \
    ( \
      PGXL_MSG_MAX_MODULES * \
      PGXL_MSG_MAX_FILEIDS_PER_MODULE * \
      PGXL_MSG_MAX_MSGIDS_PER_FILE \
    )

#define SizeOfMsgModuleCtlStruct    \
    ( \
      offsetof(MsgModuleCtlStruct, mm_flags) + \
      StartOfBackendFlags + \
      MaxBackends \
    )
static MsgModuleCtlStruct *MsgModuleCtl;
#endif

/*
 * in_error_recursion_trouble --- are we at risk of infinite error recursion?
 *
 * This function exists to provide common control of various fallback steps
 * that we take if we think we are facing infinite error recursion.  See the
 * callers for details.
 */
bool
in_error_recursion_trouble(void)
{
    /* Pull the plug if recurse more than once */
    return (recursion_depth > 2);
}

/*
 * One of those fallback steps is to stop trying to localize the error
 * message, since there's a significant probability that that's exactly
 * what's causing the recursion.
 */
static inline const char *
err_gettext(const char *str)
{
#ifdef ENABLE_NLS
    if (in_error_recursion_trouble())
        return str;
    else
        return gettext(str);
#else
    return str;
#endif
}


/*
 * errstart --- begin an error-reporting cycle
 *
 * Create a stack entry and store the given parameters in it.  Subsequently,
 * errmsg() and perhaps other routines will be called to further populate
 * the stack entry.  Finally, errfinish() will be called to actually process
 * the error report.
 *
 * Returns TRUE in normal case.  Returns FALSE to short-circuit the error
 * report (if it's a warning or lower and not to be reported anywhere).
 */
bool
errstart(int elevel, const char *filename, int lineno,
#ifdef USE_MODULE_MSGIDS
        int moduleid, int fileid, int msgid,
#endif
         const char *funcname, const char *domain)
{// #lizard forgives
    ErrorData  *edata;
    bool        output_to_server;
    bool        output_to_client = false;
    int            i;

    /*
     * Check some cases in which we want to promote an error into a more
     * severe error.  None of this logic applies for non-error messages.
     */
    if (elevel >= ERROR)
    {
        /*
         * If we are inside a critical section, all errors become PANIC
         * errors.  See miscadmin.h.
         */
        if (CritSectionCount > 0)
            elevel = PANIC;

        /*
         * Check reasons for treating ERROR as FATAL:
         *
         * 1. we have no handler to pass the error to (implies we are in the
         * postmaster or in backend startup).
         *
         * 2. ExitOnAnyError mode switch is set (initdb uses this).
         *
         * 3. the error occurred after proc_exit has begun to run.  (It's
         * proc_exit's responsibility to see that this doesn't turn into
         * infinite recursion!)
         */
        if (elevel == ERROR)
        {
            if (PG_exception_stack == NULL ||
                ExitOnAnyError ||
                proc_exit_inprogress)
                elevel = FATAL;
        }

        /*
         * If the error level is ERROR or more, errfinish is not going to
         * return to caller; therefore, if there is any stacked error already
         * in progress it will be lost.  This is more or less okay, except we
         * do not want to have a FATAL or PANIC error downgraded because the
         * reporting process was interrupted by a lower-grade error.  So check
         * the stack and make sure we panic if panic is warranted.
         */
        for (i = 0; i <= errordata_stack_depth; i++)
            elevel = Max(elevel, errordata[i].elevel);
    }

    /*
     * Now decide whether we need to process this report at all; if it's
     * warning or less and not enabled for logging, just return FALSE without
     * starting up any error logging machinery.
     */

    /* Determine whether message is enabled for server log output */
    output_to_server = is_log_level_output(elevel,
#ifdef USE_MODULE_MSGIDS
            moduleid,
            fileid,
            msgid,
#endif
            log_min_messages);

    /* Determine whether message is enabled for client output */
    if (whereToSendOutput == DestRemote && elevel != LOG_SERVER_ONLY)
    {
        /*
         * client_min_messages is honored only after we complete the
         * authentication handshake.  This is required both for security
         * reasons and because many clients can't handle NOTICE messages
         * during authentication.
         */
        if (ClientAuthInProgress)
            output_to_client = (elevel >= ERROR);
        else
            output_to_client = (elevel >= client_min_messages ||
                                elevel == INFO);
    }

    /* Skip processing effort if non-error message will not be output */
    if (elevel < ERROR && !output_to_server && !output_to_client)
        return false;

    /*
     * We need to do some actual work.  Make sure that memory context
     * initialization has finished, else we can't do anything useful.
     */
    if (ErrorContext == NULL)
    {
        /* Oops, hard crash time; very little we can do safely here */
        write_stderr("error occurred at %s:%d before error message processing is available\n",
                     filename ? filename : "(unknown file)", lineno);
        exit(2);
    }

    /*
     * Okay, crank up a stack entry to store the info in.
     */

    if (recursion_depth++ > 0 && elevel >= ERROR)
    {
        /*
         * Oops, error during error processing.  Clear ErrorContext as
         * discussed at top of file.  We will not return to the original
         * error's reporter or handler, so we don't need it.
         */
        MemoryContextReset(ErrorContext);

        /*
         * Infinite error recursion might be due to something broken in a
         * context traceback routine.  Abandon them too.  We also abandon
         * attempting to print the error statement (which, if long, could
         * itself be the source of the recursive failure).
         */
        if (in_error_recursion_trouble())
        {
            error_context_stack = NULL;
            debug_query_string = NULL;
        }
    }
    if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
    {
        /*
         * Wups, stack not big enough.  We treat this as a PANIC condition
         * because it suggests an infinite loop of errors during error
         * recovery.
         */
        errordata_stack_depth = -1; /* make room on stack */
        ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
    }

    /* Initialize data for this error frame */
    edata = &errordata[errordata_stack_depth];
    MemSet(edata, 0, sizeof(ErrorData));
    edata->elevel = elevel;
    edata->output_to_server = output_to_server;
    edata->output_to_client = output_to_client;
    if (filename)
    {
        const char *slash;

        /* keep only base name, useful especially for vpath builds */
        slash = strrchr(filename, '/');
        if (slash)
            filename = slash + 1;
    }
    edata->filename = filename;
    edata->lineno = lineno;
    edata->funcname = funcname;
    /* the default text domain is the backend's */
    edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
    /* initialize context_domain the same way (see set_errcontext_domain()) */
    edata->context_domain = edata->domain;
    /* Select default errcode based on elevel */
    if (elevel >= ERROR)
        edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
    else if (elevel == WARNING)
        edata->sqlerrcode = ERRCODE_WARNING;
    else
        edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
    /* errno is saved here so that error parameter eval can't change it */
    edata->saved_errno = errno;

    /*
     * Any allocations for this error state level should go into ErrorContext
     */
    edata->assoc_context = ErrorContext;

    recursion_depth--;
    return true;
}

/*
 * errfinish --- end an error-reporting cycle
 *
 * Produce the appropriate error report(s) and pop the error stack.
 *
 * If elevel is ERROR or worse, control does not return to the caller.
 * See elog.h for the error level definitions.
 */
void
errfinish(int dummy,...)
{// #lizard forgives
    ErrorData  *edata = &errordata[errordata_stack_depth];
    int            elevel;
    MemoryContext oldcontext;
    ErrorContextCallback *econtext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    elevel = edata->elevel;

    /*
     * Do processing in ErrorContext, which we hope has enough reserved space
     * to report an error.
     */
    oldcontext = MemoryContextSwitchTo(ErrorContext);

    /*
     * Call any context callback functions.  Errors occurring in callback
     * functions will be treated as recursive errors --- this ensures we will
     * avoid infinite recursion (see errstart).
     */
    for (econtext = error_context_stack;
         econtext != NULL;
         econtext = econtext->previous)
        (*econtext->callback) (econtext->arg);

    /*
     * If ERROR (not more nor less) we pass it off to the current handler.
     * Printing it and popping the stack is the responsibility of the handler.
     */
    if (elevel == ERROR)
    {
        /*
         * We do some minimal cleanup before longjmp'ing so that handlers can
         * execute in a reasonably sane state.
         *
         * Reset InterruptHoldoffCount in case we ereport'd from inside an
         * interrupt holdoff section.  (We assume here that no handler will
         * itself be inside a holdoff section.  If necessary, such a handler
         * could save and restore InterruptHoldoffCount for itself, but this
         * should make life easier for most.)
         */
        InterruptHoldoffCount = 0;
        QueryCancelHoldoffCount = 0;

        CritSectionCount = 0;    /* should be unnecessary, but... */

        /*
         * Note that we leave CurrentMemoryContext set to ErrorContext. The
         * handler should reset it to something else soon.
         */

        recursion_depth--;
        
        PG_RE_THROW();
    }

    /*
     * If we are doing FATAL or PANIC, abort any old-style COPY OUT in
     * progress, so that we can report the message before dying.  (Without
     * this, pq_putmessage will refuse to send the message at all, which is
     * what we want for NOTICE messages, but not for fatal exits.) This hack
     * is necessary because of poor design of old-style copy protocol.  Note
     * we must do this even if client is fool enough to have set
     * client_min_messages above FATAL, so don't look at output_to_client.
     */
    if (elevel >= FATAL && whereToSendOutput == DestRemote)
        pq_endcopyout(true);

    /* Emit the message to the right places */
    EmitErrorReport();

    /* Now free up subsidiary data attached to stack entry, and release it */
    if (edata->message)
        pfree(edata->message);
    if (edata->detail)
        pfree(edata->detail);
    if (edata->detail_log)
        pfree(edata->detail_log);
    if (edata->hint)
        pfree(edata->hint);
    if (edata->context)
        pfree(edata->context);
    if (edata->schema_name)
        pfree(edata->schema_name);
    if (edata->table_name)
        pfree(edata->table_name);
    if (edata->column_name)
        pfree(edata->column_name);
    if (edata->datatype_name)
        pfree(edata->datatype_name);
    if (edata->constraint_name)
        pfree(edata->constraint_name);
    if (edata->internalquery)
        pfree(edata->internalquery);

    errordata_stack_depth--;

    /* Exit error-handling context */
    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;

    /*
     * Perform error recovery action as specified by elevel.
     */
    if (elevel == FATAL)
    {
        /*
         * For a FATAL error, we let proc_exit clean up and exit.
         *
         * If we just reported a startup failure, the client will disconnect
         * on receiving it, so don't send any more to the client.
         */
        if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)
            whereToSendOutput = DestNone;

        /*
         * fflush here is just to improve the odds that we get to see the
         * error message, in case things are so hosed that proc_exit crashes.
         * Any other code you might be tempted to add here should probably be
         * in an on_proc_exit or on_shmem_exit callback instead.
         */
        fflush(stdout);
        fflush(stderr);

#ifdef __OPENTENBASE__
        /*
          * parallel worker exit abnormally, tell other workers
                 */
        if (IsParallelWorker())
        {
            ParallelSendEreport();
            ParallelHashJoinEreport();
            ReDistributeEreport();

            ParallelDsmDetach();
        }
#endif

        /*
         * Do normal process-exit cleanup, then return exit code 1 to indicate
         * FATAL termination.  The postmaster may or may not consider this
         * worthy of panic, depending on which subprocess returns it.
         */
        proc_exit(1);
    }

    if (PANIC  == elevel)
    {
        /*
         * Serious crash time. Postmaster will observe SIGABRT process exit
         * status and kill the other backends too.
         *
         * XXX: what if we are *in* the postmaster?  abort() won't kill our
         * children...
         */
        fflush(stdout);
        fflush(stderr);
        abort();
    }
    
#ifdef __OPENTENBASE__
    if (STOP == elevel)
    {
        /*
         * Serious stop time. Postmaster will observe STOP process exit
         * status 3 and kill the other backends too. Doing this is to ensure transaction security.
         */
        fflush(stdout);
        fflush(stderr);
        exit(3);
    }
#endif

    /*
     * Check for cancel/die interrupt first --- this is so that the user can
     * stop a query emitting tons of notice or warning messages, even if it's
     * in a loop that otherwise fails to check for interrupts.
     */
    CHECK_FOR_INTERRUPTS();
}


/*
 * errcode --- add SQLSTATE error code to the current error
 *
 * The code is expected to be represented as per MAKE_SQLSTATE().
 */
int
errcode(int sqlerrcode)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->sqlerrcode = sqlerrcode;

    return 0;                    /* return value does not matter */
}


/*
 * errcode_for_file_access --- add SQLSTATE error code to the current error
 *
 * The SQLSTATE code is chosen based on the saved errno value.  We assume
 * that the failing operation was some type of disk file access.
 *
 * NOTE: the primary error message string should generally include %m
 * when this is used.
 */
int
errcode_for_file_access(void)
{// #lizard forgives
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    switch (edata->saved_errno)
    {
            /* Permission-denied failures */
        case EPERM:                /* Not super-user */
        case EACCES:            /* Permission denied */
#ifdef EROFS
        case EROFS:                /* Read only file system */
#endif
            edata->sqlerrcode = ERRCODE_INSUFFICIENT_PRIVILEGE;
            break;

            /* File not found */
        case ENOENT:            /* No such file or directory */
            edata->sqlerrcode = ERRCODE_UNDEFINED_FILE;
            break;

            /* Duplicate file */
        case EEXIST:            /* File exists */
            edata->sqlerrcode = ERRCODE_DUPLICATE_FILE;
            break;

            /* Wrong object type or state */
        case ENOTDIR:            /* Not a directory */
        case EISDIR:            /* Is a directory */
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
        case ENOTEMPTY:            /* Directory not empty */
#endif
            edata->sqlerrcode = ERRCODE_WRONG_OBJECT_TYPE;
            break;

            /* Insufficient resources */
        case ENOSPC:            /* No space left on device */
            edata->sqlerrcode = ERRCODE_DISK_FULL;
            break;

        case ENFILE:            /* File table overflow */
        case EMFILE:            /* Too many open files */
            edata->sqlerrcode = ERRCODE_INSUFFICIENT_RESOURCES;
            break;

            /* Hardware failure */
        case EIO:                /* I/O error */
            edata->sqlerrcode = ERRCODE_IO_ERROR;
            break;

            /* All else is classified as internal errors */
        default:
            edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
            break;
    }

    return 0;                    /* return value does not matter */
}

/*
 * errcode_for_socket_access --- add SQLSTATE error code to the current error
 *
 * The SQLSTATE code is chosen based on the saved errno value.  We assume
 * that the failing operation was some type of socket access.
 *
 * NOTE: the primary error message string should generally include %m
 * when this is used.
 */
int
errcode_for_socket_access(void)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    switch (edata->saved_errno)
    {
            /* Loss of connection */
        case EPIPE:
#ifdef ECONNRESET
        case ECONNRESET:
#endif
            edata->sqlerrcode = ERRCODE_CONNECTION_FAILURE;
            break;

            /* All else is classified as internal errors */
        default:
            edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
            break;
    }

    return 0;                    /* return value does not matter */
}


/*
 * This macro handles expansion of a format string and associated parameters;
 * it's common code for errmsg(), errdetail(), etc.  Must be called inside
 * a routine that is declared like "const char *fmt, ..." and has an edata
 * pointer set up.  The message is assigned to edata->targetfield, or
 * appended to it if appendval is true.  The message is subject to translation
 * if translateit is true.
 *
 * Note: we pstrdup the buffer rather than just transferring its storage
 * to the edata field because the buffer might be considerably larger than
 * really necessary.
 */
#define EVALUATE_MESSAGE(domain, targetfield, appendval, translateit)    \
    { \
        char           *fmtbuf; \
        StringInfoData    buf; \
        /* Internationalize the error format string */ \
        if ((translateit) && !in_error_recursion_trouble()) \
            fmt = dgettext((domain), fmt);                  \
        /* Expand %m in format string */ \
        fmtbuf = expand_fmt_string(fmt, edata); \
        initStringInfo(&buf); \
        if ((appendval) && edata->targetfield) { \
            appendStringInfoString(&buf, edata->targetfield); \
            appendStringInfoChar(&buf, '\n'); \
        } \
        /* Generate actual output --- have to use appendStringInfoVA */ \
        for (;;) \
        { \
            va_list        args; \
            int            needed; \
            va_start(args, fmt); \
            needed = appendStringInfoVA(&buf, fmtbuf, args); \
            va_end(args); \
            if (needed == 0) \
                break; \
            enlargeStringInfo(&buf, needed); \
        } \
        /* Done with expanded fmt */ \
        pfree(fmtbuf); \
        /* Save the completed message into the stack item */ \
        if (edata->targetfield) \
            pfree(edata->targetfield); \
        edata->targetfield = pstrdup(buf.data); \
        pfree(buf.data); \
    }

/*
 * Same as above, except for pluralized error messages.  The calling routine
 * must be declared like "const char *fmt_singular, const char *fmt_plural,
 * unsigned long n, ...".  Translation is assumed always wanted.
 */
#define EVALUATE_MESSAGE_PLURAL(domain, targetfield, appendval)  \
    { \
        const char       *fmt; \
        char           *fmtbuf; \
        StringInfoData    buf; \
        /* Internationalize the error format string */ \
        if (!in_error_recursion_trouble()) \
            fmt = dngettext((domain), fmt_singular, fmt_plural, n); \
        else \
            fmt = (n == 1 ? fmt_singular : fmt_plural); \
        /* Expand %m in format string */ \
        fmtbuf = expand_fmt_string(fmt, edata); \
        initStringInfo(&buf); \
        if ((appendval) && edata->targetfield) { \
            appendStringInfoString(&buf, edata->targetfield); \
            appendStringInfoChar(&buf, '\n'); \
        } \
        /* Generate actual output --- have to use appendStringInfoVA */ \
        for (;;) \
        { \
            va_list        args; \
            int            needed; \
            va_start(args, n); \
            needed = appendStringInfoVA(&buf, fmtbuf, args); \
            va_end(args); \
            if (needed == 0) \
                break; \
            enlargeStringInfo(&buf, needed); \
        } \
        /* Done with expanded fmt */ \
        pfree(fmtbuf); \
        /* Save the completed message into the stack item */ \
        if (edata->targetfield) \
            pfree(edata->targetfield); \
        edata->targetfield = pstrdup(buf.data); \
        pfree(buf.data); \
    }


/*
 * errmsg --- add a primary error message text to the current error
 *
 * In addition to the usual %-escapes recognized by printf, "%m" in
 * fmt is replaced by the error message for the caller's value of errno.
 *
 * Note: no newline is needed at the end of the fmt string, since
 * ereport will provide one for the output methods that need it.
 */
int
errmsg(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    edata->message_id = fmt;
    EVALUATE_MESSAGE(edata->domain, message, false, true);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errmsg_internal --- add a primary error message text to the current error
 *
 * This is exactly like errmsg() except that strings passed to errmsg_internal
 * are not translated, and are customarily left out of the
 * internationalization message dictionary.  This should be used for "can't
 * happen" cases that are probably not worth spending translation effort on.
 * We also use this for certain cases where we *must* not try to translate
 * the message because the translation would fail and result in infinite
 * error recursion.
 */
int
errmsg_internal(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    edata->message_id = fmt;
    EVALUATE_MESSAGE(edata->domain, message, false, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errmsg_plural --- add a primary error message text to the current error,
 * with support for pluralization of the message text
 */
int
errmsg_plural(const char *fmt_singular, const char *fmt_plural,
              unsigned long n,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    edata->message_id = fmt_singular;
    EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errdetail --- add a detail error message text to the current error
 */
int
errdetail(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE(edata->domain, detail, false, true);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errdetail_internal --- add a detail error message text to the current error
 *
 * This is exactly like errdetail() except that strings passed to
 * errdetail_internal are not translated, and are customarily left out of the
 * internationalization message dictionary.  This should be used for detail
 * messages that seem not worth translating for one reason or another
 * (typically, that they don't seem to be useful to average users).
 */
int
errdetail_internal(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE(edata->domain, detail, false, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errdetail_log --- add a detail_log error message text to the current error
 */
int
errdetail_log(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE(edata->domain, detail_log, false, true);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}

/*
 * errdetail_log_plural --- add a detail_log error message text to the current error
 * with support for pluralization of the message text
 */
int
errdetail_log_plural(const char *fmt_singular, const char *fmt_plural,
                     unsigned long n,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE_PLURAL(edata->domain, detail_log, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errdetail_plural --- add a detail error message text to the current error,
 * with support for pluralization of the message text
 */
int
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
                 unsigned long n,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errhint --- add a hint error message text to the current error
 */
int
errhint(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE(edata->domain, hint, false, true);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}


/*
 * errcontext_msg --- add a context error message text to the current error
 *
 * Unlike other cases, multiple calls are allowed to build up a stack of
 * context information.  We assume earlier calls represent more-closely-nested
 * states.
 */
int
errcontext_msg(const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    EVALUATE_MESSAGE(edata->context_domain, context, true, true);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
    return 0;                    /* return value does not matter */
}

/*
 * set_errcontext_domain --- set message domain to be used by errcontext()
 *
 * errcontext_msg() can be called from a different module than the original
 * ereport(), so we cannot use the message domain passed in errstart() to
 * translate it.  Instead, each errcontext_msg() call should be preceded by
 * a set_errcontext_domain() call to specify the domain.  This is usually
 * done transparently by the errcontext() macro.
 *
 * Although errcontext is primarily meant for use at call sites distant from
 * the original ereport call, there are a few places that invoke errcontext
 * within ereport.  The expansion of errcontext as a comma expression calling
 * set_errcontext_domain then errcontext_msg is problematic in this case,
 * because the intended comma expression becomes two arguments to errfinish,
 * which the compiler is at liberty to evaluate in either order.  But in
 * such a case, the set_errcontext_domain calls must be selecting the same
 * TEXTDOMAIN value that the errstart call did, so order does not matter
 * so long as errstart initializes context_domain along with domain.
 */
int
set_errcontext_domain(const char *domain)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    /* the default text domain is the backend's */
    edata->context_domain = domain ? domain : PG_TEXTDOMAIN("postgres");

    return 0;                    /* return value does not matter */
}


/*
 * errhidestmt --- optionally suppress STATEMENT: field of log entry
 *
 * This should be called if the message text already includes the statement.
 */
int
errhidestmt(bool hide_stmt)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->hide_stmt = hide_stmt;

    return 0;                    /* return value does not matter */
}

/*
 * errhidecontext --- optionally suppress CONTEXT: field of log entry
 *
 * This should only be used for verbose debugging messages where the repeated
 * inclusion of context would bloat the log volume too much.
 */
int
errhidecontext(bool hide_ctx)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->hide_ctx = hide_ctx;

    return 0;                    /* return value does not matter */
}


/*
 * errfunction --- add reporting function name to the current error
 *
 * This is used when backwards compatibility demands that the function
 * name appear in messages sent to old-protocol clients.  Note that the
 * passed string is expected to be a non-freeable constant string.
 */
int
errfunction(const char *funcname)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->funcname = funcname;
    edata->show_funcname = true;

    return 0;                    /* return value does not matter */
}

/*
 * errposition --- add cursor position to the current error
 */
int
errposition(int cursorpos)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->cursorpos = cursorpos;

    return 0;                    /* return value does not matter */
}

/*
 * internalerrposition --- add internal cursor position to the current error
 */
int
internalerrposition(int cursorpos)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    edata->internalpos = cursorpos;

    return 0;                    /* return value does not matter */
}

/*
 * internalerrquery --- add internal query text to the current error
 *
 * Can also pass NULL to drop the internal query text entry.  This case
 * is intended for use in error callback subroutines that are editorializing
 * on the layout of the error report.
 */
int
internalerrquery(const char *query)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    if (edata->internalquery)
    {
        pfree(edata->internalquery);
        edata->internalquery = NULL;
    }

    if (query)
        edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);

    return 0;                    /* return value does not matter */
}

/*
 * err_generic_string -- used to set individual ErrorData string fields
 * identified by PG_DIAG_xxx codes.
 *
 * This intentionally only supports fields that don't use localized strings,
 * so that there are no translation considerations.
 *
 * Most potential callers should not use this directly, but instead prefer
 * higher-level abstractions, such as errtablecol() (see relcache.c).
 */
int
err_generic_string(int field, const char *str)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    switch (field)
    {
        case PG_DIAG_SCHEMA_NAME:
            set_errdata_field(edata->assoc_context, &edata->schema_name, str);
            break;
        case PG_DIAG_TABLE_NAME:
            set_errdata_field(edata->assoc_context, &edata->table_name, str);
            break;
        case PG_DIAG_COLUMN_NAME:
            set_errdata_field(edata->assoc_context, &edata->column_name, str);
            break;
        case PG_DIAG_DATATYPE_NAME:
            set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
            break;
        case PG_DIAG_CONSTRAINT_NAME:
            set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
            break;
        default:
            elog(ERROR, "unsupported ErrorData field id: %d", field);
            break;
    }

    return 0;                    /* return value does not matter */
}

/*
 * set_errdata_field --- set an ErrorData string field
 */
static void
set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
{
    Assert(*ptr == NULL);
    *ptr = MemoryContextStrdup(cxt, str);
}

/*
 * geterrcode --- return the currently set SQLSTATE error code
 *
 * This is only intended for use in error callback subroutines, since there
 * is no other place outside elog.c where the concept is meaningful.
 */
int
geterrcode(void)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    return edata->sqlerrcode;
}

/*
 * geterrposition --- return the currently set error position (0 if none)
 *
 * This is only intended for use in error callback subroutines, since there
 * is no other place outside elog.c where the concept is meaningful.
 */
int
geterrposition(void)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    return edata->cursorpos;
}

/*
 * getinternalerrposition --- same for internal error position
 *
 * This is only intended for use in error callback subroutines, since there
 * is no other place outside elog.c where the concept is meaningful.
 */
int
getinternalerrposition(void)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];

    /* we don't bother incrementing recursion_depth */
    CHECK_STACK_DEPTH();

    return edata->internalpos;
}


/*
 * elog_start --- startup for old-style API
 *
 * All that we do here is stash the hidden filename/lineno/funcname
 * arguments into a stack entry, along with the current value of errno.
 *
 * We need this to be separate from elog_finish because there's no other
 * C89-compliant way to deal with inserting extra arguments into the elog
 * call.  (When using C99's __VA_ARGS__, we could possibly merge this with
 * elog_finish, but there doesn't seem to be a good way to save errno before
 * evaluating the format arguments if we do that.)
 */
void
elog_start(const char *filename, int lineno,
#ifdef USE_MODULE_MSGIDS
        int moduleid, int fileid, int msgid,
#endif
        const char *funcname)
{
    ErrorData  *edata;

    /* Make sure that memory context initialization has finished */
    if (ErrorContext == NULL)
    {
        /* Oops, hard crash time; very little we can do safely here */
        write_stderr("error occurred at %s:%d before error message processing is available\n",
                     filename ? filename : "(unknown file)", lineno);
        exit(2);
    }

    if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
    {
        /*
         * Wups, stack not big enough.  We treat this as a PANIC condition
         * because it suggests an infinite loop of errors during error
         * recovery.  Note that the message is intentionally not localized,
         * else failure to convert it to client encoding could cause further
         * recursion.
         */
        errordata_stack_depth = -1; /* make room on stack */
        ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
    }

    edata = &errordata[errordata_stack_depth];
    if (filename)
    {
        const char *slash;

        /* keep only base name, useful especially for vpath builds */
        slash = strrchr(filename, '/');
        if (slash)
            filename = slash + 1;
    }
    edata->filename = filename;
    edata->lineno = lineno;
    edata->funcname = funcname;
#ifdef USE_MODULE_MSGIDS
    edata->moduleid = moduleid;
    edata->fileid = fileid;
    edata->msgid = msgid;
#endif
    /* errno is saved now so that error parameter eval can't change it */
    edata->saved_errno = errno;

    /* Use ErrorContext for any allocations done at this level. */
    edata->assoc_context = ErrorContext;
}

/*
 * elog_finish --- finish up for old-style API
 */
void
elog_finish(int elevel, const char *fmt,...)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    CHECK_STACK_DEPTH();

    /*
     * Do errstart() to see if we actually want to report the message.
     */
    errordata_stack_depth--;
    errno = edata->saved_errno;
    if (!errstart(elevel, edata->filename, edata->lineno,
#ifdef USE_MODULE_MSGIDS
                edata->moduleid,
                edata->fileid, edata->msgid,
#endif
                edata->funcname, NULL))
        return;                    /* nothing to do */

    /*
     * Format error message just like errmsg_internal().
     */
    recursion_depth++;
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    edata->message_id = fmt;
    EVALUATE_MESSAGE(edata->domain, message, false, false);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;

    /*
     * And let errfinish() finish up.
     */
    errfinish(0);
}


/*
 * Functions to allow construction of error message strings separately from
 * the ereport() call itself.
 *
 * The expected calling convention is
 *
 *    pre_format_elog_string(errno, domain), var = format_elog_string(format,...)
 *
 * which can be hidden behind a macro such as GUC_check_errdetail().  We
 * assume that any functions called in the arguments of format_elog_string()
 * cannot result in re-entrant use of these functions --- otherwise the wrong
 * text domain might be used, or the wrong errno substituted for %m.  This is
 * okay for the current usage with GUC check hooks, but might need further
 * effort someday.
 *
 * The result of format_elog_string() is stored in ErrorContext, and will
 * therefore survive until FlushErrorState() is called.
 */
static int    save_format_errnumber;
static const char *save_format_domain;

void
pre_format_elog_string(int errnumber, const char *domain)
{
    /* Save errno before evaluation of argument functions can change it */
    save_format_errnumber = errnumber;
    /* Save caller's text domain */
    save_format_domain = domain;
}

char *
format_elog_string(const char *fmt,...)
{
    ErrorData    errdata;
    ErrorData  *edata;
    MemoryContext oldcontext;

    /* Initialize a mostly-dummy error frame */
    edata = &errdata;
    MemSet(edata, 0, sizeof(ErrorData));
    /* the default text domain is the backend's */
    edata->domain = save_format_domain ? save_format_domain : PG_TEXTDOMAIN("postgres");
    /* set the errno to be used to interpret %m */
    edata->saved_errno = save_format_errnumber;

    oldcontext = MemoryContextSwitchTo(ErrorContext);

    edata->message_id = fmt;
    EVALUATE_MESSAGE(edata->domain, message, false, true);

    MemoryContextSwitchTo(oldcontext);

    return edata->message;
}


/*
 * Actual output of the top-of-stack error message
 *
 * In the ereport(ERROR) case this is called from PostgresMain (or not at all,
 * if the error is caught by somebody).  For all other severity levels this
 * is called by errfinish.
 */
void
EmitErrorReport(void)
{
    ErrorData  *edata = &errordata[errordata_stack_depth];
    MemoryContext oldcontext;

    recursion_depth++;
    CHECK_STACK_DEPTH();
    oldcontext = MemoryContextSwitchTo(edata->assoc_context);

    /*
     * Call hook before sending message to log.  The hook function is allowed
     * to turn off edata->output_to_server, so we must recheck that afterward.
     * Making any other change in the content of edata is not considered
     * supported.
     *
     * Note: the reason why the hook can only turn off output_to_server, and
     * not turn it on, is that it'd be unreliable: we will never get here at
     * all if errstart() deems the message uninteresting.  A hook that could
     * make decisions in that direction would have to hook into errstart(),
     * where it would have much less information available.  emit_log_hook is
     * intended for custom log filtering and custom log message transmission
     * mechanisms.
     *
     * The log hook has access to both the translated and original English
     * error message text, which is passed through to allow it to be used as a
     * message identifier. Note that the original text is not available for
     * detail, detail_log, hint and context text elements.
     */
    if (edata->output_to_server && emit_log_hook)
        (*emit_log_hook) (edata);

    /* Send to server log, if enabled */
    if (edata->output_to_server)
        send_message_to_server_log(edata);

    /* Send to client, if enabled */
    if (edata->output_to_client)
    {
        if (true == g_enable_copy_silence ||
			(IS_PGXC_DATANODE && edata->elevel < ERROR))
        {
			/*
			 * Do not send nonfatal msg to client for Datanode.
			 *
			 * It is possible that DataPumpRawSendData is sending data now,
			 * and this msg can be mixed with data message
			 * if the socket is written concurrently.
			 *
			 * In addition, the msg is not that important.
			 */
            ;
        }
        else
        {
			/*
			 * For the same reason as above, shut down producer for Datanode
			 * before send ERROR/FATAL msg.
			 * It is ok to shut down it again in AbortTransaction.
			 */
			if (IS_PGXC_DATANODE)
			{
				SqueueProducerExit();
			}
            send_message_to_frontend(edata);
        }
    }

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;
}

/*
 * CopyErrorData --- obtain a copy of the topmost error stack entry
 *
 * This is only for use in error handler code.  The data is copied into the
 * current memory context, so callers should always switch away from
 * ErrorContext first; otherwise it will be lost when FlushErrorState is done.
 */
ErrorData *
CopyErrorData(void)
{// #lizard forgives
    ErrorData  *edata = &errordata[errordata_stack_depth];
    ErrorData  *newedata;

    /*
     * we don't increment recursion_depth because out-of-memory here does not
     * indicate a problem within the error subsystem.
     */
    CHECK_STACK_DEPTH();

    Assert(CurrentMemoryContext != ErrorContext);

    /* Copy the struct itself */
    newedata = (ErrorData *) palloc(sizeof(ErrorData));
    memcpy(newedata, edata, sizeof(ErrorData));

    /* Make copies of separately-allocated fields */
    if (newedata->message)
        newedata->message = pstrdup(newedata->message);
    if (newedata->detail)
        newedata->detail = pstrdup(newedata->detail);
    if (newedata->detail_log)
        newedata->detail_log = pstrdup(newedata->detail_log);
    if (newedata->hint)
        newedata->hint = pstrdup(newedata->hint);
    if (newedata->context)
        newedata->context = pstrdup(newedata->context);
    if (newedata->schema_name)
        newedata->schema_name = pstrdup(newedata->schema_name);
    if (newedata->table_name)
        newedata->table_name = pstrdup(newedata->table_name);
    if (newedata->column_name)
        newedata->column_name = pstrdup(newedata->column_name);
    if (newedata->datatype_name)
        newedata->datatype_name = pstrdup(newedata->datatype_name);
    if (newedata->constraint_name)
        newedata->constraint_name = pstrdup(newedata->constraint_name);
    if (newedata->internalquery)
        newedata->internalquery = pstrdup(newedata->internalquery);

    /* Use the calling context for string allocation */
    newedata->assoc_context = CurrentMemoryContext;

    return newedata;
}

/*
 * FreeErrorData --- free the structure returned by CopyErrorData.
 *
 * Error handlers should use this in preference to assuming they know all
 * the separately-allocated fields.
 */
void
FreeErrorData(ErrorData *edata)
{// #lizard forgives
    if (edata->message)
        pfree(edata->message);
    if (edata->detail)
        pfree(edata->detail);
    if (edata->detail_log)
        pfree(edata->detail_log);
    if (edata->hint)
        pfree(edata->hint);
    if (edata->context)
        pfree(edata->context);
    if (edata->schema_name)
        pfree(edata->schema_name);
    if (edata->table_name)
        pfree(edata->table_name);
    if (edata->column_name)
        pfree(edata->column_name);
    if (edata->datatype_name)
        pfree(edata->datatype_name);
    if (edata->constraint_name)
        pfree(edata->constraint_name);
    if (edata->internalquery)
        pfree(edata->internalquery);
    pfree(edata);
}

/*
 * FlushErrorState --- flush the error state after error recovery
 *
 * This should be called by an error handler after it's done processing
 * the error; or as soon as it's done CopyErrorData, if it intends to
 * do stuff that is likely to provoke another error.  You are not "out" of
 * the error subsystem until you have done this.
 */
void
FlushErrorState(void)
{
    /*
     * Reset stack to empty.  The only case where it would be more than one
     * deep is if we serviced an error that interrupted construction of
     * another message.  We assume control escaped out of that message
     * construction and won't ever go back.
     */
    errordata_stack_depth = -1;
    recursion_depth = 0;
    /* Delete all data in ErrorContext */
    MemoryContextResetAndDeleteChildren(ErrorContext);
}

/*
 * ThrowErrorData --- report an error described by an ErrorData structure
 *
 * This is somewhat like ReThrowError, but it allows elevels besides ERROR,
 * and the boolean flags such as output_to_server are computed via the
 * default rules rather than being copied from the given ErrorData.
 * This is primarily used to re-report errors originally reported by
 * background worker processes and then propagated (with or without
 * modification) to the backend responsible for them.
 */
void
ThrowErrorData(ErrorData *edata)
{// #lizard forgives
    ErrorData  *newedata;
    MemoryContext oldcontext;

    if (!errstart(edata->elevel, edata->filename, edata->lineno,
#ifdef USE_MODULE_MSGIDS
                edata->moduleid,
                edata->fileid, edata->msgid,
#endif
                  edata->funcname, NULL))
        return;                    /* error is not to be reported at all */

    newedata = &errordata[errordata_stack_depth];
    recursion_depth++;
    oldcontext = MemoryContextSwitchTo(newedata->assoc_context);

    /* Copy the supplied fields to the error stack entry. */
    if (edata->sqlerrcode != 0)
        newedata->sqlerrcode = edata->sqlerrcode;
    if (edata->message)
        newedata->message = pstrdup(edata->message);
    if (edata->detail)
        newedata->detail = pstrdup(edata->detail);
    if (edata->detail_log)
        newedata->detail_log = pstrdup(edata->detail_log);
    if (edata->hint)
        newedata->hint = pstrdup(edata->hint);
    if (edata->context)
        newedata->context = pstrdup(edata->context);
    /* assume message_id is not available */
    if (edata->schema_name)
        newedata->schema_name = pstrdup(edata->schema_name);
    if (edata->table_name)
        newedata->table_name = pstrdup(edata->table_name);
    if (edata->column_name)
        newedata->column_name = pstrdup(edata->column_name);
    if (edata->datatype_name)
        newedata->datatype_name = pstrdup(edata->datatype_name);
    if (edata->constraint_name)
        newedata->constraint_name = pstrdup(edata->constraint_name);
    newedata->cursorpos = edata->cursorpos;
    newedata->internalpos = edata->internalpos;
    if (edata->internalquery)
        newedata->internalquery = pstrdup(edata->internalquery);

    MemoryContextSwitchTo(oldcontext);
    recursion_depth--;

    /* Process the error. */
    errfinish(0);
}

/*
 * ReThrowError --- re-throw a previously copied error
 *
 * A handler can do CopyErrorData/FlushErrorState to get out of the error
 * subsystem, then do some processing, and finally ReThrowError to re-throw
 * the original error.  This is slower than just PG_RE_THROW() but should
 * be used if the "some processing" is likely to incur another error.
 */
void
ReThrowError(ErrorData *edata)
{// #lizard forgives
    ErrorData  *newedata;

    Assert(edata->elevel == ERROR);

    /* Push the data back into the error context */
    recursion_depth++;
    MemoryContextSwitchTo(ErrorContext);

    if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
    {
        /*
         * Wups, stack not big enough.  We treat this as a PANIC condition
         * because it suggests an infinite loop of errors during error
         * recovery.
         */
        errordata_stack_depth = -1; /* make room on stack */
        ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
    }

    newedata = &errordata[errordata_stack_depth];
    memcpy(newedata, edata, sizeof(ErrorData));

    /* Make copies of separately-allocated fields */
    if (newedata->message)
        newedata->message = pstrdup(newedata->message);
    if (newedata->detail)
        newedata->detail = pstrdup(newedata->detail);
    if (newedata->detail_log)
        newedata->detail_log = pstrdup(newedata->detail_log);
    if (newedata->hint)
        newedata->hint = pstrdup(newedata->hint);
    if (newedata->context)
        newedata->context = pstrdup(newedata->context);
    if (newedata->schema_name)
        newedata->schema_name = pstrdup(newedata->schema_name);
    if (newedata->table_name)
        newedata->table_name = pstrdup(newedata->table_name);
    if (newedata->column_name)
        newedata->column_name = pstrdup(newedata->column_name);
    if (newedata->datatype_name)
        newedata->datatype_name = pstrdup(newedata->datatype_name);
    if (newedata->constraint_name)
        newedata->constraint_name = pstrdup(newedata->constraint_name);
    if (newedata->internalquery)
        newedata->internalquery = pstrdup(newedata->internalquery);

    /* Reset the assoc_context to be ErrorContext */
    newedata->assoc_context = ErrorContext;

    recursion_depth--;
    PG_RE_THROW();
}

/*
 * pg_re_throw --- out-of-line implementation of PG_RE_THROW() macro
 */
void
pg_re_throw(void)
{
    /* If possible, throw the error to the next outer setjmp handler */
    if (PG_exception_stack != NULL)
        siglongjmp(*PG_exception_stack, 1);
    else
    {
        /*
         * If we get here, elog(ERROR) was thrown inside a PG_TRY block, which
         * we have now exited only to discover that there is no outer setjmp
         * handler to pass the error to.  Had the error been thrown outside
         * the block to begin with, we'd have promoted the error to FATAL, so
         * the correct behavior is to make it FATAL now; that is, emit it and
         * then call proc_exit.
         */
        ErrorData  *edata = &errordata[errordata_stack_depth];

        Assert(errordata_stack_depth >= 0);
        Assert(edata->elevel == ERROR);
        edata->elevel = FATAL;

        /*
         * At least in principle, the increase in severity could have changed
         * where-to-output decisions, so recalculate.  This should stay in
         * sync with errstart(), which see for comments.
         */
        if (IsPostmasterEnvironment)
            edata->output_to_server = is_log_level_output(FATAL,
#ifdef USE_MODULE_MSGIDS
                    0,
                    0,
                    0,
#endif
                    log_min_messages);
        else
            edata->output_to_server = (FATAL >= log_min_messages);
        if (whereToSendOutput == DestRemote)
        {
            if (ClientAuthInProgress)
                edata->output_to_client = true;
            else
                edata->output_to_client = (FATAL >= client_min_messages);
        }

        /*
         * We can use errfinish() for the rest, but we don't want it to call
         * any error context routines a second time.  Since we know we are
         * about to exit, it should be OK to just clear the context stack.
         */
        error_context_stack = NULL;

        errfinish(0);
    }

    /* Doesn't return ... */
    ExceptionalCondition("pg_re_throw tried to return", "FailedAssertion",
                         __FILE__, __LINE__);
}


/*
 * GetErrorContextStack - Return the context stack, for display/diags
 *
 * Returns a pstrdup'd string in the caller's context which includes the PG
 * error call stack.  It is the caller's responsibility to ensure this string
 * is pfree'd (or its context cleaned up) when done.
 *
 * This information is collected by traversing the error contexts and calling
 * each context's callback function, each of which is expected to call
 * errcontext() to return a string which can be presented to the user.
 */
char *
GetErrorContextStack(void)
{
    ErrorData  *edata;
    ErrorContextCallback *econtext;

    /*
     * Okay, crank up a stack entry to store the info in.
     */
    recursion_depth++;

    if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
    {
        /*
         * Wups, stack not big enough.  We treat this as a PANIC condition
         * because it suggests an infinite loop of errors during error
         * recovery.
         */
        errordata_stack_depth = -1; /* make room on stack */
        ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
    }

    /*
     * Things look good so far, so initialize our error frame
     */
    edata = &errordata[errordata_stack_depth];
    MemSet(edata, 0, sizeof(ErrorData));

    /*
     * Set up assoc_context to be the caller's context, so any allocations
     * done (which will include edata->context) will use their context.
     */
    edata->assoc_context = CurrentMemoryContext;

    /*
     * Call any context callback functions to collect the context information
     * into edata->context.
     *
     * Errors occurring in callback functions should go through the regular
     * error handling code which should handle any recursive errors, though we
     * double-check above, just in case.
     */
    for (econtext = error_context_stack;
         econtext != NULL;
         econtext = econtext->previous)
        (*econtext->callback) (econtext->arg);

    /*
     * Clean ourselves off the stack, any allocations done should have been
     * using edata->assoc_context, which we set up earlier to be the caller's
     * context, so we're free to just remove our entry off the stack and
     * decrement recursion depth and exit.
     */
    errordata_stack_depth--;
    recursion_depth--;

    /*
     * Return a pointer to the string the caller asked for, which should have
     * been allocated in their context.
     */
    return edata->context;
}


/*
 * Initialization of error output file
 */
void
DebugFileOpen(void)
{
    int            fd,
                istty;

    if (OutputFileName[0])
    {
        /*
         * A debug-output file name was given.
         *
         * Make sure we can write the file, and find out if it's a tty.
         */
        if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
                       0666)) < 0)
            ereport(FATAL,
                    (errcode_for_file_access(),
                     errmsg("could not open file \"%s\": %m", OutputFileName)));
        istty = isatty(fd);
        close(fd);

        /*
         * Redirect our stderr to the debug output file.
         */
        if (!freopen(OutputFileName, "a", stderr))
            ereport(FATAL,
                    (errcode_for_file_access(),
                     errmsg("could not reopen file \"%s\" as stderr: %m",
                            OutputFileName)));

        /*
         * If the file is a tty and we're running under the postmaster, try to
         * send stdout there as well (if it isn't a tty then stderr will block
         * out stdout, so we may as well let stdout go wherever it was going
         * before).
         */
        if (istty && IsUnderPostmaster)
            if (!freopen(OutputFileName, "a", stdout))
                ereport(FATAL,
                        (errcode_for_file_access(),
                         errmsg("could not reopen file \"%s\" as stdout: %m",
                                OutputFileName)));
    }
}


#ifdef HAVE_SYSLOG

/*
 * Set or update the parameters for syslog logging
 */
void
set_syslog_parameters(const char *ident, int facility)
{
    /*
     * guc.c is likely to call us repeatedly with same parameters, so don't
     * thrash the syslog connection unnecessarily.  Also, we do not re-open
     * the connection until needed, since this routine will get called whether
     * or not Log_destination actually mentions syslog.
     *
     * Note that we make our own copy of the ident string rather than relying
     * on guc.c's.  This may be overly paranoid, but it ensures that we cannot
     * accidentally free a string that syslog is still using.
     */
    if (syslog_ident == NULL || strcmp(syslog_ident, ident) != 0 ||
        syslog_facility != facility)
    {
        if (openlog_done)
        {
            closelog();
            openlog_done = false;
        }
        if (syslog_ident)
            free(syslog_ident);
        syslog_ident = strdup(ident);
        /* if the strdup fails, we will cope in write_syslog() */
        syslog_facility = facility;
    }
}


/*
 * Write a message line to syslog
 */
static void
write_syslog(int level, const char *line)
{// #lizard forgives
    static unsigned long seq = 0;

    int            len;
    const char *nlpos;

    /* Open syslog connection if not done yet */
    if (!openlog_done)
    {
        openlog(syslog_ident ? syslog_ident : "postgres",
                LOG_PID | LOG_NDELAY | LOG_NOWAIT,
                syslog_facility);
        openlog_done = true;
    }

    /*
     * We add a sequence number to each log message to suppress "same"
     * messages.
     */
    seq++;

    /*
     * Our problem here is that many syslog implementations don't handle long
     * messages in an acceptable manner. While this function doesn't help that
     * fact, it does work around by splitting up messages into smaller pieces.
     *
     * We divide into multiple syslog() calls if message is too long or if the
     * message contains embedded newline(s).
     */
    len = strlen(line);
    nlpos = strchr(line, '\n');
    if (syslog_split_messages && (len > PG_SYSLOG_LIMIT || nlpos != NULL))
    {
        int            chunk_nr = 0;

        while (len > 0)
        {
            char        buf[PG_SYSLOG_LIMIT + 1];
            int            buflen;
            int            i;

            /* if we start at a newline, move ahead one char */
            if (line[0] == '\n')
            {
                line++;
                len--;
                /* we need to recompute the next newline's position, too */
                nlpos = strchr(line, '\n');
                continue;
            }

            /* copy one line, or as much as will fit, to buf */
            if (nlpos != NULL)
                buflen = nlpos - line;
            else
                buflen = len;
            buflen = Min(buflen, PG_SYSLOG_LIMIT);
            memcpy(buf, line, buflen);
            buf[buflen] = '\0';

            /* trim to multibyte letter boundary */
            buflen = pg_mbcliplen(buf, buflen, buflen);
            if (buflen <= 0)
                return;
            buf[buflen] = '\0';

            /* already word boundary? */
            if (line[buflen] != '\0' &&
                !isspace((unsigned char) line[buflen]))
            {
                /* try to divide at word boundary */
                i = buflen - 1;
                while (i > 0 && !isspace((unsigned char) buf[i]))
                    i--;

                if (i > 0)        /* else couldn't divide word boundary */
                {
                    buflen = i;
                    buf[i] = '\0';
                }
            }

            chunk_nr++;

            if (syslog_sequence_numbers)
                syslog(level, "[%lu-%d] %s", seq, chunk_nr, buf);
            else
                syslog(level, "[%d] %s", chunk_nr, buf);

            line += buflen;
            len -= buflen;
        }
    }
    else
    {
        /* message short enough */
        if (syslog_sequence_numbers)
            syslog(level, "[%lu] %s", seq, line);
        else
            syslog(level, "%s", line);
    }
}
#endif                            /* HAVE_SYSLOG */

#ifdef WIN32
/*
 * Get the PostgreSQL equivalent of the Windows ANSI code page.  "ANSI" system
 * interfaces (e.g. CreateFileA()) expect string arguments in this encoding.
 * Every process in a given system will find the same value at all times.
 */
static int
GetACPEncoding(void)
{
    static int    encoding = -2;

    if (encoding == -2)
        encoding = pg_codepage_to_encoding(GetACP());

    return encoding;
}

/*
 * Write a message line to the windows event log
 */
static void
write_eventlog(int level, const char *line, int len)
{// #lizard forgives
    WCHAR       *utf16;
    int            eventlevel = EVENTLOG_ERROR_TYPE;
    static HANDLE evtHandle = INVALID_HANDLE_VALUE;

    if (evtHandle == INVALID_HANDLE_VALUE)
    {
        evtHandle = RegisterEventSource(NULL,
                                        event_source ? event_source : DEFAULT_EVENT_SOURCE);
        if (evtHandle == NULL)
        {
            evtHandle = INVALID_HANDLE_VALUE;
            return;
        }
    }

    switch (level)
    {
        case DEBUG5:
        case DEBUG4:
        case DEBUG3:
        case DEBUG2:
        case DEBUG1:
        case LOG:
        case LOG_SERVER_ONLY:
        case INFO:
        case NOTICE:
            eventlevel = EVENTLOG_INFORMATION_TYPE;
            break;
        case WARNING:
            eventlevel = EVENTLOG_WARNING_TYPE;
            break;
        case ERROR:
        case FATAL:
        case PANIC:
        default:
            eventlevel = EVENTLOG_ERROR_TYPE;
            break;
    }

    /*
     * If message character encoding matches the encoding expected by
     * ReportEventA(), call it to avoid the hazards of conversion.  Otherwise,
     * try to convert the message to UTF16 and write it with ReportEventW().
     * Fall back on ReportEventA() if conversion failed.
     *
     * Also verify that we are not on our way into error recursion trouble due
     * to error messages thrown deep inside pgwin32_message_to_UTF16().
     */
    if (!in_error_recursion_trouble() &&
        GetMessageEncoding() != GetACPEncoding())
    {
        utf16 = pgwin32_message_to_UTF16(line, len, NULL);
        if (utf16)
        {
            ReportEventW(evtHandle,
                         eventlevel,
                         0,
                         0,        /* All events are Id 0 */
                         NULL,
                         1,
                         0,
                         (LPCWSTR *) &utf16,
                         NULL);
            /* XXX Try ReportEventA() when ReportEventW() fails? */

            pfree(utf16);
            return;
        }
    }
    ReportEventA(evtHandle,
                 eventlevel,
                 0,
                 0,                /* All events are Id 0 */
                 NULL,
                 1,
                 0,
                 &line,
                 NULL);
}
#endif                            /* WIN32 */

static void
write_console(const char *line, int len)
{
    int            rc;

#ifdef WIN32

    /*
     * Try to convert the message to UTF16 and write it with WriteConsoleW().
     * Fall back on write() if anything fails.
     *
     * In contrast to write_eventlog(), don't skip straight to write() based
     * on the applicable encodings.  Unlike WriteConsoleW(), write() depends
     * on the suitability of the console output code page.  Since we put
     * stderr into binary mode in SubPostmasterMain(), write() skips the
     * necessary translation anyway.
     *
     * WriteConsoleW() will fail if stderr is redirected, so just fall through
     * to writing unconverted to the logfile in this case.
     *
     * Since we palloc the structure required for conversion, also fall
     * through to writing unconverted if we have not yet set up
     * CurrentMemoryContext.
     */
    if (!in_error_recursion_trouble() &&
        !redirection_done &&
        CurrentMemoryContext != NULL)
    {
        WCHAR       *utf16;
        int            utf16len;

        utf16 = pgwin32_message_to_UTF16(line, len, &utf16len);
        if (utf16 != NULL)
        {
            HANDLE        stdHandle;
            DWORD        written;

            stdHandle = GetStdHandle(STD_ERROR_HANDLE);
            if (WriteConsoleW(stdHandle, utf16, utf16len, &written, NULL))
            {
                pfree(utf16);
                return;
            }

            /*
             * In case WriteConsoleW() failed, fall back to writing the
             * message unconverted.
             */
            pfree(utf16);
        }
    }
#else

    /*
     * Conversion on non-win32 platforms is not implemented yet. It requires
     * non-throw version of pg_do_encoding_conversion(), that converts
     * unconvertable characters to '?' without errors.
     */
#endif

    /*
     * We ignore any error from write() here.  We have no useful way to report
     * it ... certainly whining on stderr isn't likely to be productive.
     */
    rc = write(fileno(stderr), line, len);
    (void) rc;
}

/*
 * setup formatted_log_time, for consistent times between CSV and regular logs
 */
static void
setup_formatted_log_time(void)
{
    pg_time_t    stamp_time;
    char        msbuf[13];

    if (!saved_timeval_set)
    {
        gettimeofday(&saved_timeval, NULL);
        saved_timeval_set = true;
    }

    stamp_time = (pg_time_t) saved_timeval.tv_sec;

    /*
     * Note: we expect that guc.c will ensure that log_timezone is set up (at
     * least with a minimal GMT value) before Log_line_prefix can become
     * nonempty or CSV mode can be selected.
     */
    pg_strftime(formatted_log_time, FORMATTED_TS_LEN,
    /* leave room for milliseconds... */
                "%Y-%m-%d %H:%M:%S     %Z",
                pg_localtime(&stamp_time, log_timezone));

    /* 'paste' milliseconds into place... */
    sprintf(msbuf, ".%03d", (int) (saved_timeval.tv_usec / 1000));
    memcpy(formatted_log_time + 19, msbuf, 4);
}

/*
 * setup formatted_start_time
 */
static void
setup_formatted_start_time(void)
{
    pg_time_t    stamp_time = (pg_time_t) MyStartTime;

    /*
     * Note: we expect that guc.c will ensure that log_timezone is set up (at
     * least with a minimal GMT value) before Log_line_prefix can become
     * nonempty or CSV mode can be selected.
     */
    pg_strftime(formatted_start_time, FORMATTED_TS_LEN,
                "%Y-%m-%d %H:%M:%S %Z",
                pg_localtime(&stamp_time, log_timezone));
}

/*
 * process_log_prefix_padding --- helper function for processing the format
 * string in log_line_prefix
 *
 * Note: This function returns NULL if it finds something which
 * it deems invalid in the format string.
 */
static const char *
process_log_prefix_padding(const char *p, int *ppadding)
{
    int            paddingsign = 1;
    int            padding = 0;

    if (*p == '-')
    {
        p++;

        if (*p == '\0')            /* Did the buf end in %- ? */
            return NULL;
        paddingsign = -1;
    }

    /* generate an int version of the numerical string */
    while (*p >= '0' && *p <= '9')
        padding = padding * 10 + (*p++ - '0');

    /* format is invalid if it ends with the padding number */
    if (*p == '\0')
        return NULL;

    padding *= paddingsign;
    *ppadding = padding;
    return p;
}

/*
 * Format tag info for log lines; append to the provided buffer.
 */
static void
log_line_prefix(StringInfo buf, ErrorData *edata)
{// #lizard forgives
    /* static counter for line numbers */
    static long log_line_number = 0;

    /* has counter been reset in current process? */
    static int    log_my_pid = 0;
    int            padding;
    const char *p;

    /*
     * This is one of the few places where we'd rather not inherit a static
     * variable's value from the postmaster.  But since we will, reset it when
     * MyProcPid changes. MyStartTime also changes when MyProcPid does, so
     * reset the formatted start timestamp too.
     */
    if (log_my_pid != MyProcPid)
    {
        log_line_number = 0;
        log_my_pid = MyProcPid;
        formatted_start_time[0] = '\0';
    }
    log_line_number++;

    if (Log_line_prefix == NULL)
        return;                    /* in case guc hasn't run yet */

    for (p = Log_line_prefix; *p != '\0'; p++)
    {
        if (*p != '%')
        {
            /* literal char, just copy */
            appendStringInfoChar(buf, *p);
            continue;
        }

        /* must be a '%', so skip to the next char */
        p++;
        if (*p == '\0')
            break;                /* format error - ignore it */
        else if (*p == '%')
        {
            /* string contains %% */
            appendStringInfoChar(buf, '%');
            continue;
        }


        /*
         * Process any formatting which may exist after the '%'.  Note that
         * process_log_prefix_padding moves p past the padding number if it
         * exists.
         *
         * Note: Since only '-', '0' to '9' are valid formatting characters we
         * can do a quick check here to pre-check for formatting. If the char
         * is not formatting then we can skip a useless function call.
         *
         * Further note: At least on some platforms, passing %*s rather than
         * %s to appendStringInfo() is substantially slower, so many of the
         * cases below avoid doing that unless non-zero padding is in fact
         * specified.
         */
        if (*p > '9')
            padding = 0;
        else if ((p = process_log_prefix_padding(p, &padding)) == NULL)
            break;

        /* process the option */
        switch (*p)
        {
            case 'a':
                if (MyProcPort)
                {
                    const char *appname = application_name;

                    if (appname == NULL || *appname == '\0')
                        appname = _("[unknown]");
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, appname);
                    else
                        appendStringInfoString(buf, appname);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);

                break;
            case 'u':
                if (MyProcPort)
                {
                    const char *username = MyProcPort->user_name;

                    if (username == NULL || *username == '\0')
                        username = _("[unknown]");
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, username);
                    else
                        appendStringInfoString(buf, username);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'd':
                if (MyProcPort)
                {
                    const char *dbname = MyProcPort->database_name;

                    if (dbname == NULL || *dbname == '\0')
                        dbname = _("[unknown]");
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, dbname);
                    else
                        appendStringInfoString(buf, dbname);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'c':
                if (padding != 0)
                {
                    char        strfbuf[128];

					snprintf(strfbuf, sizeof(strfbuf) - 1, "%lx.%x,coord(%d.%u)",
							 (long) (MyStartTime), MyProcPid, 
							 pgxc_get_coordinator_proc_pid(),
							 pgxc_get_coordinator_proc_vxid());
					appendStringInfo(buf, "%*s", padding, strfbuf);
				}
				else
					appendStringInfo(buf, "%lx.%x,coord(%d.%u)",
									(long) (MyStartTime), MyProcPid, 
									pgxc_get_coordinator_proc_pid(),
									pgxc_get_coordinator_proc_vxid());
				break;
			case 'p':
				if (padding != 0)
					appendStringInfo(buf, "%*d", padding, MyProcPid);
				else
					appendStringInfo(buf, "%d,coord(%d.%u)",
									MyProcPid, 
									pgxc_get_coordinator_proc_pid(),
									pgxc_get_coordinator_proc_vxid());
                break;
            case 'l':
                if (padding != 0)
                    appendStringInfo(buf, "%*ld", padding, log_line_number);
                else
                    appendStringInfo(buf, "%ld", log_line_number);
                break;
            case 'm':
                setup_formatted_log_time();
                if (padding != 0)
                    appendStringInfo(buf, "%*s", padding, formatted_log_time);
                else
                    appendStringInfoString(buf, formatted_log_time);
                break;
            case 't':
                {
                    pg_time_t    stamp_time = (pg_time_t) time(NULL);
                    char        strfbuf[128];

                    pg_strftime(strfbuf, sizeof(strfbuf),
                                "%Y-%m-%d %H:%M:%S %Z",
                                pg_localtime(&stamp_time, log_timezone));
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, strfbuf);
                    else
                        appendStringInfoString(buf, strfbuf);
                }
                break;
            case 'n':
                {
                    char        strfbuf[128];

                    if (!saved_timeval_set)
                    {
                        gettimeofday(&saved_timeval, NULL);
                        saved_timeval_set = true;
                    }

                    snprintf(strfbuf, sizeof(strfbuf), "%ld.%03d",
                             (long) saved_timeval.tv_sec,
                             (int) (saved_timeval.tv_usec / 1000));

                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, strfbuf);
                    else
                        appendStringInfoString(buf, strfbuf);
                }
                break;
            case 's':
                if (formatted_start_time[0] == '\0')
                    setup_formatted_start_time();
                if (padding != 0)
                    appendStringInfo(buf, "%*s", padding, formatted_start_time);
                else
                    appendStringInfoString(buf, formatted_start_time);
                break;
            case 'i':
                if (MyProcPort)
                {
                    const char *psdisp;
                    int            displen;

                    psdisp = get_ps_display(&displen);
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, psdisp);
                    else
                        appendBinaryStringInfo(buf, psdisp, displen);

                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'r':
                if (MyProcPort && MyProcPort->remote_host)
                {
                    if (padding != 0)
                    {
                        if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
                        {
                            /*
                             * This option is slightly special as the port
                             * number may be appended onto the end. Here we
                             * need to build 1 string which contains the
                             * remote_host and optionally the remote_port (if
                             * set) so we can properly align the string.
                             */

                            char       *hostport;

                            hostport = psprintf("%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port);
                            appendStringInfo(buf, "%*s", padding, hostport);
                            pfree(hostport);
                        }
                        else
                            appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
                    }
                    else
                    {
                        /* padding is 0, so we don't need a temp buffer */
                        appendStringInfoString(buf, MyProcPort->remote_host);
                        if (MyProcPort->remote_port &&
                            MyProcPort->remote_port[0] != '\0')
                            appendStringInfo(buf, "(%s)",
                                             MyProcPort->remote_port);
                    }

                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'h':
                if (MyProcPort && MyProcPort->remote_host)
                {
                    if (padding != 0)
                        appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
                    else
                        appendStringInfoString(buf, MyProcPort->remote_host);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'q':
                /* in postmaster and friends, stop if %q is seen */
                /* in a backend, just ignore */
                if (MyProcPort == NULL)
                    return;
                break;
            case 'v':
                /* keep VXID format in sync with lockfuncs.c */
                if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
                {
                    if (padding != 0)
                    {
                        char        strfbuf[128];

                        snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
                                 MyProc->backendId, MyProc->lxid);
                        appendStringInfo(buf, "%*s", padding, strfbuf);
                    }
                    else
                        appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;
            case 'x':
                if (padding != 0)
                    appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
                else
                    appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
                break;
            case 'e':
                if (padding != 0)
                    appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode));
                else
                    appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
                break;
#ifdef XCP
            case 'R':
                if (padding != 0)
                {
                    char        strfbuf[128];

                    snprintf(strfbuf, sizeof(strfbuf) - 1, "(%c/%s/%d)",
                            remoteConnType == REMOTE_CONN_APP ? 'A' : 
                            remoteConnType == REMOTE_CONN_DATANODE ? 'D' :
                            remoteConnType == REMOTE_CONN_COORD ? 'C' : 'U',
                            parentPGXCNode, parentPGXCPid);
                    appendStringInfo(buf, "%*s", padding, strfbuf);
                }
                else
                    appendStringInfo(buf, "(%c/%s/%d)",
                            remoteConnType == REMOTE_CONN_APP ? 'A' : 
                            remoteConnType == REMOTE_CONN_DATANODE ? 'D' :
                            remoteConnType == REMOTE_CONN_COORD ? 'C' : 'U',
                            parentPGXCNode, parentPGXCPid);
                break;

            case 'C':
                if (MyProc != NULL)
                {
                    if (padding != 0)
                    {
                        char        strfbuf[128];

                        snprintf(strfbuf, sizeof(strfbuf) - 1, "(%s/%u)",
                                 MyCoordName, MyProc->coordPid);
                        appendStringInfo(buf, "%*s", padding, strfbuf);
                    }
                    else
                        appendStringInfo(buf, "(%s/%u)", MyCoordName, MyProc->coordPid);
                }
                else if (padding != 0)
                    appendStringInfoSpaces(buf,
                                           padding > 0 ? padding : -padding);
                break;

            case 'S':
                if (padding != 0)
                    appendStringInfo(buf, "%*s", padding, global_session_string);
                else
                    appendStringInfo(buf, "%s", global_session_string);

                break;
#endif                
            default:
                /* format error - ignore it */
                break;
        }
    }
}

/*
 * append a CSV'd version of a string to a StringInfo
 * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
 * If it's NULL, append nothing.
 */
static inline void
appendCSVLiteral(StringInfo buf, const char *data)
{
    const char *p = data;
    char        c;

    /* avoid confusing an empty string with NULL */
    if (p == NULL)
        return;

    appendStringInfoCharMacro(buf, '"');
    while ((c = *p++) != '\0')
    {
        if (c == '"')
            appendStringInfoCharMacro(buf, '"');
        appendStringInfoCharMacro(buf, c);
    }
    appendStringInfoCharMacro(buf, '"');
}

/*
 * Constructs the error message, depending on the Errordata it gets, in a CSV
 * format which is described in doc/src/sgml/config.sgml.
 */
static void
write_csvlog(ErrorData *edata)
{
	StringInfoData buf;
	bool		print_stmt = false;

	/* static counter for line numbers */
	static long log_line_number = 0;

	/* has counter been reset in current process? */
	static int	log_my_pid = 0;

	/*
	 * This is one of the few places where we'd rather not inherit a static
	 * variable's value from the postmaster.  But since we will, reset it when
	 * MyProcPid changes.
	 */
	if (log_my_pid != MyProcPid)
	{
		log_line_number = 0;
		log_my_pid = MyProcPid;
		formatted_start_time[0] = '\0';
	}
	log_line_number++;

	initStringInfo(&buf);

	/*
	 * timestamp with milliseconds
	 *
	 * Check if the timestamp is already calculated for the syslog message,
	 * and use it if so.  Otherwise, get the current timestamp.  This is done
	 * to put same timestamp in both syslog and csvlog messages.
	 */
	if (formatted_log_time[0] == '\0')
		setup_formatted_log_time();

	appendStringInfoString(&buf, formatted_log_time);
	appendStringInfoChar(&buf, ',');

	/* username */
	if (MyProcPort)
		appendCSVLiteral(&buf, MyProcPort->user_name);
	appendStringInfoChar(&buf, ',');

	/* database name */
	if (MyProcPort)
		appendCSVLiteral(&buf, MyProcPort->database_name);
	appendStringInfoChar(&buf, ',');

	/* Process id  */
	if (MyProcPid != 0)
		appendStringInfo(&buf, "%d,coord(%d.%u)",
						MyProcPid,
						pgxc_get_coordinator_proc_pid(),
						pgxc_get_coordinator_proc_vxid());
	appendStringInfoChar(&buf, ',');

	/* Remote host and port */
	if (MyProcPort && MyProcPort->remote_host)
	{
		appendStringInfoChar(&buf, '"');
		appendStringInfoString(&buf, MyProcPort->remote_host);
		if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
		{
			appendStringInfoChar(&buf, ':');
			appendStringInfoString(&buf, MyProcPort->remote_port);
		}
		appendStringInfoChar(&buf, '"');
	}
	appendStringInfoChar(&buf, ',');

	/* session id */
	appendStringInfo(&buf, "%lx.%x,coord(%d.%u)",
					(long) MyStartTime, MyProcPid,
					pgxc_get_coordinator_proc_pid(),
					pgxc_get_coordinator_proc_vxid());
    appendStringInfoChar(&buf, ',');

    /* Line number */
    appendStringInfo(&buf, "%ld", log_line_number);
    appendStringInfoChar(&buf, ',');

    /* PS display */
    if (MyProcPort)
    {
        StringInfoData msgbuf;
        const char *psdisp;
        int            displen;

        initStringInfo(&msgbuf);

        psdisp = get_ps_display(&displen);
        appendBinaryStringInfo(&msgbuf, psdisp, displen);
        appendCSVLiteral(&buf, msgbuf.data);

        pfree(msgbuf.data);
    }
    appendStringInfoChar(&buf, ',');

    /* session start timestamp */
    if (formatted_start_time[0] == '\0')
        setup_formatted_start_time();
    appendStringInfoString(&buf, formatted_start_time);
    appendStringInfoChar(&buf, ',');

    /* Virtual transaction id */
    /* keep VXID format in sync with lockfuncs.c */
    if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
        appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
    appendStringInfoChar(&buf, ',');

    /* Transaction id */
    appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
    appendStringInfoChar(&buf, ',');

    /* Error severity */
    appendStringInfoString(&buf, _(error_severity(edata->elevel)));
    appendStringInfoChar(&buf, ',');

    /* SQL state code */
    appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
    appendStringInfoChar(&buf, ',');

    /* errmessage */
    appendCSVLiteral(&buf, edata->message);
    appendStringInfoChar(&buf, ',');

    /* errdetail or errdetail_log */
    if (edata->detail_log)
        appendCSVLiteral(&buf, edata->detail_log);
    else
        appendCSVLiteral(&buf, edata->detail);
    appendStringInfoChar(&buf, ',');

    /* errhint */
    appendCSVLiteral(&buf, edata->hint);
    appendStringInfoChar(&buf, ',');

    /* internal query */
    appendCSVLiteral(&buf, edata->internalquery);
    appendStringInfoChar(&buf, ',');

    /* if printed internal query, print internal pos too */
    if (edata->internalpos > 0 && edata->internalquery != NULL)
        appendStringInfo(&buf, "%d", edata->internalpos);
    appendStringInfoChar(&buf, ',');

    /* errcontext */
    if (!edata->hide_ctx)
        appendCSVLiteral(&buf, edata->context);
    appendStringInfoChar(&buf, ',');

    /* user query --- only reported if not disabled by the caller */
    if (is_log_level_output(edata->elevel,
#ifdef USE_MODULE_MSGIDS
                edata->moduleid,
                edata->fileid,
                edata->msgid,
#endif
                log_min_error_statement) &&
        debug_query_string != NULL &&
        !edata->hide_stmt)
        print_stmt = true;
    if (print_stmt)
        appendCSVLiteral(&buf, debug_query_string);
    appendStringInfoChar(&buf, ',');
    if (print_stmt && edata->cursorpos > 0)
        appendStringInfo(&buf, "%d", edata->cursorpos);
    appendStringInfoChar(&buf, ',');

    /* file error location */
    if (Log_error_verbosity >= PGERROR_VERBOSE)
    {
        StringInfoData msgbuf;

        initStringInfo(&msgbuf);

        if (edata->funcname && edata->filename)
            appendStringInfo(&msgbuf, "%s, %s:%d",
                             edata->funcname, edata->filename,
                             edata->lineno);
        else if (edata->filename)
            appendStringInfo(&msgbuf, "%s:%d",
                             edata->filename, edata->lineno);
        appendCSVLiteral(&buf, msgbuf.data);
        pfree(msgbuf.data);
    }
    appendStringInfoChar(&buf, ',');

    /* application name */
    if (application_name)
        appendCSVLiteral(&buf, application_name);

    appendStringInfoChar(&buf, '\n');

    /* If in the syslogger process, try to write messages direct to file */
    if (am_syslogger)
        write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
    else
        write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);

    pfree(buf.data);
}

/*
 * Unpack MAKE_SQLSTATE code. Note that this returns a pointer to a
 * static buffer.
 */
char *
unpack_sql_state(int sql_state)
{
    static char buf[12];
    int            i;

    for (i = 0; i < 5; i++)
    {
        buf[i] = PGUNSIXBIT(sql_state);
        sql_state >>= 6;
    }

    buf[i] = '\0';
    return buf;
}


/*
 * Write error report to server's log
 */
static void
send_message_to_server_log(ErrorData *edata)
{// #lizard forgives
    StringInfoData buf;

    initStringInfo(&buf);

    saved_timeval_set = false;
    formatted_log_time[0] = '\0';

    log_line_prefix(&buf, edata);
    appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));

    if (Log_error_verbosity >= PGERROR_VERBOSE)
        appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));

    if (edata->message)
        append_with_tabs(&buf, edata->message);
    else
        append_with_tabs(&buf, _("missing error text"));

    if (edata->cursorpos > 0)
        appendStringInfo(&buf, _(" at character %d"),
                         edata->cursorpos);
    else if (edata->internalpos > 0)
        appendStringInfo(&buf, _(" at character %d"),
                         edata->internalpos);

    appendStringInfoChar(&buf, '\n');

    if (Log_error_verbosity >= PGERROR_DEFAULT)
    {
        if (edata->detail_log)
        {
            log_line_prefix(&buf, edata);
            appendStringInfoString(&buf, _("DETAIL:  "));
            append_with_tabs(&buf, edata->detail_log);
            appendStringInfoChar(&buf, '\n');
        }
        else if (edata->detail)
        {
            log_line_prefix(&buf, edata);
            appendStringInfoString(&buf, _("DETAIL:  "));
            append_with_tabs(&buf, edata->detail);
            appendStringInfoChar(&buf, '\n');
        }
        if (edata->hint)
        {
            log_line_prefix(&buf, edata);
            appendStringInfoString(&buf, _("HINT:  "));
            append_with_tabs(&buf, edata->hint);
            appendStringInfoChar(&buf, '\n');
        }
        if (edata->internalquery)
        {
            log_line_prefix(&buf, edata);
            appendStringInfoString(&buf, _("QUERY:  "));
            append_with_tabs(&buf, edata->internalquery);
            appendStringInfoChar(&buf, '\n');
        }
        if (edata->context && !edata->hide_ctx)
        {
            log_line_prefix(&buf, edata);
            appendStringInfoString(&buf, _("CONTEXT:  "));
            append_with_tabs(&buf, edata->context);
            appendStringInfoChar(&buf, '\n');
        }
        if (Log_error_verbosity >= PGERROR_VERBOSE)
        {
            /* assume no newlines in funcname or filename... */
            if (edata->funcname && edata->filename)
            {
                log_line_prefix(&buf, edata);
                appendStringInfo(&buf, _("LOCATION:  %s, %s:%d\n"),
                                 edata->funcname, edata->filename,
                                 edata->lineno);
            }
            else if (edata->filename)
            {
                log_line_prefix(&buf, edata);
                appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
                                 edata->filename, edata->lineno);
            }
        }
    }

    /*
     * If the user wants the query that generated this error logged, do it.
     */
    if (is_log_level_output(edata->elevel,
#ifdef USE_MODULE_MSGIDS
                edata->moduleid,
                edata->fileid,
                edata->msgid,
#endif
                log_min_error_statement) &&
        debug_query_string != NULL &&
        !edata->hide_stmt)
    {
        log_line_prefix(&buf, edata);
        appendStringInfoString(&buf, _("STATEMENT:  "));
        append_with_tabs(&buf, debug_query_string);
        appendStringInfoChar(&buf, '\n');
    }
#if 0
    /* for error code contrib */
    if (g_pfErrcodeHook && (false == g_is_in_init_phase) && (NULL != MyProc))
    {
        g_pfErrcodeHook(edata, &buf);
    }
#endif

#ifdef HAVE_SYSLOG
    /* Write to syslog, if enabled */
    if (Log_destination & LOG_DESTINATION_SYSLOG)
    {
        int            syslog_level;

        switch (edata->elevel)
        {
            case DEBUG5:
            case DEBUG4:
            case DEBUG3:
            case DEBUG2:
            case DEBUG1:
                syslog_level = LOG_DEBUG;
                break;
            case LOG:
            case LOG_SERVER_ONLY:
            case INFO:
                syslog_level = LOG_INFO;
                break;
            case NOTICE:
            case WARNING:
                syslog_level = LOG_NOTICE;
                break;
            case ERROR:
                syslog_level = LOG_WARNING;
                break;
            case FATAL:
                syslog_level = LOG_ERR;
                break;
            case PANIC:
            default:
                syslog_level = LOG_CRIT;
                break;
        }

        write_syslog(syslog_level, buf.data);
    }
#endif                            /* HAVE_SYSLOG */

#ifdef WIN32
    /* Write to eventlog, if enabled */
    if (Log_destination & LOG_DESTINATION_EVENTLOG)
    {
        write_eventlog(edata->elevel, buf.data, buf.len);
    }
#endif                            /* WIN32 */

    /* Write to stderr, if enabled */
    if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == DestDebug)
    {
        /*
         * Use the chunking protocol if we know the syslogger should be
         * catching stderr output, and we are not ourselves the syslogger.
         * Otherwise, just do a vanilla write to stderr.
         */
        if (redirection_done && !am_syslogger)
            write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
#ifdef WIN32

        /*
         * In a win32 service environment, there is no usable stderr. Capture
         * anything going there and write it to the eventlog instead.
         *
         * If stderr redirection is active, it was OK to write to stderr above
         * because that's really a pipe to the syslogger process.
         */
        else if (pgwin32_is_service())
            write_eventlog(edata->elevel, buf.data, buf.len);
#endif
        else
            write_console(buf.data, buf.len);
    }

    /* If in the syslogger process, try to write messages direct to file */
    if (am_syslogger)
        write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR);

    /* Write to CSV log if enabled */
    if (Log_destination & LOG_DESTINATION_CSVLOG)
    {
        if (redirection_done || am_syslogger)
        {
            /*
             * send CSV data if it's safe to do so (syslogger doesn't need the
             * pipe). First get back the space in the message buffer.
             */
            pfree(buf.data);
            write_csvlog(edata);
        }
        else
        {
            /*
             * syslogger not up (yet), so just dump the message to stderr,
             * unless we already did so above.
             */
            if (!(Log_destination & LOG_DESTINATION_STDERR) &&
                whereToSendOutput != DestDebug)
                write_console(buf.data, buf.len);
            pfree(buf.data);
        }
    }
    else
    {
        pfree(buf.data);
    }
}

/*
 * Send data to the syslogger using the chunked protocol
 *
 * Note: when there are multiple backends writing into the syslogger pipe,
 * it's critical that each write go into the pipe indivisibly, and not
 * get interleaved with data from other processes.  Fortunately, the POSIX
 * spec requires that writes to pipes be atomic so long as they are not
 * more than PIPE_BUF bytes long.  So we divide long messages into chunks
 * that are no more than that length, and send one chunk per write() call.
 * The collector process knows how to reassemble the chunks.
 *
 * Because of the atomic write requirement, there are only two possible
 * results from write() here: -1 for failure, or the requested number of
 * bytes.  There is not really anything we can do about a failure; retry would
 * probably be an infinite loop, and we can't even report the error usefully.
 * (There is noplace else we could send it!)  So we might as well just ignore
 * the result from write().  However, on some platforms you get a compiler
 * warning from ignoring write()'s result, so do a little dance with casting
 * rc to void to shut up the compiler.
 */
static void
write_pipe_chunks(char *data, int len, int dest)
{// #lizard forgives
    PipeProtoChunk p;
    int            fd = fileno(stderr);
    int            rc;

    Assert(len > 0);

    p.proto.nuls[0] = p.proto.nuls[1] = '\0';
    p.proto.pid = MyProcPid;

    /* write all but the last chunk */
    while (len > PIPE_MAX_PAYLOAD)
    {
        p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
        p.proto.len = PIPE_MAX_PAYLOAD;
        memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
#ifdef __OPENTENBASE__
        /* use timeout to prevent write log stuck. */
        PostmasterEnableLogTimeout();
#endif
        rc = write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
        
#ifdef __OPENTENBASE__
        PostmasterDisableTimeout();
        /* if we are interruppted, just return */
        if (EINTR == errno && rc < 0)
        {
            return;
        }
#endif
        (void) rc;
        data += PIPE_MAX_PAYLOAD;
        len -= PIPE_MAX_PAYLOAD;
    }

    /* write the last chunk */
    p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
    p.proto.len = len;
    memcpy(p.proto.data, data, len);

#ifdef __OPENTENBASE__
    /* use timeout to prevent write log stuck. */
    PostmasterEnableLogTimeout();
#endif
    rc = write(fd, &p, PIPE_HEADER_SIZE + len);
#ifdef __OPENTENBASE__
    /* if we are interruppted, just return */
    PostmasterDisableTimeout();
    if (EINTR == errno && rc < 0)
    {
        return;
    }
#endif
    (void) rc;
}


/*
 * Append a text string to the error report being built for the client.
 *
 * This is ordinarily identical to pq_sendstring(), but if we are in
 * error recursion trouble we skip encoding conversion, because of the
 * possibility that the problem is a failure in the encoding conversion
 * subsystem itself.  Code elsewhere should ensure that the passed-in
 * strings will be plain 7-bit ASCII, and thus not in need of conversion,
 * in such cases.  (In particular, we disable localization of error messages
 * to help ensure that's true.)
 */
static void
err_sendstring(StringInfo buf, const char *str)
{
    if (in_error_recursion_trouble())
        pq_send_ascii_string(buf, str);
    else
        pq_sendstring(buf, str);
}

/*
 * Write error report to client
 */
static void
send_message_to_frontend(ErrorData *edata)
{// #lizard forgives
    StringInfoData msgbuf;

    /* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
    pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');

    if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
    {
        /* New style with separate fields */
        const char *sev;
        char        tbuf[12];
        int            ssval;
        int            i;

        sev = error_severity(edata->elevel);
        pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
        err_sendstring(&msgbuf, _(sev));
        pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY_NONLOCALIZED);
        err_sendstring(&msgbuf, sev);

        /* unpack MAKE_SQLSTATE code */
        ssval = edata->sqlerrcode;
        for (i = 0; i < 5; i++)
        {
            tbuf[i] = PGUNSIXBIT(ssval);
            ssval >>= 6;
        }
        tbuf[i] = '\0';

        pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
        err_sendstring(&msgbuf, tbuf);

        /* M field is required per protocol, so always send something */
        pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
        if (edata->message)
            err_sendstring(&msgbuf, edata->message);
        else
            err_sendstring(&msgbuf, _("missing error text"));

        if (edata->detail)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_DETAIL);
            err_sendstring(&msgbuf, edata->detail);
        }

        /* detail_log is intentionally not used here */

        if (edata->hint)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
            err_sendstring(&msgbuf, edata->hint);
        }

        if (edata->context)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
            err_sendstring(&msgbuf, edata->context);
        }

        if (edata->schema_name)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
            err_sendstring(&msgbuf, edata->schema_name);
        }

        if (edata->table_name)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
            err_sendstring(&msgbuf, edata->table_name);
        }

        if (edata->column_name)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAME);
            err_sendstring(&msgbuf, edata->column_name);
        }

        if (edata->datatype_name)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_DATATYPE_NAME);
            err_sendstring(&msgbuf, edata->datatype_name);
        }

        if (edata->constraint_name)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
            err_sendstring(&msgbuf, edata->constraint_name);
        }

        if (edata->cursorpos > 0)
        {
            snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
            pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
            err_sendstring(&msgbuf, tbuf);
        }

        if (edata->internalpos > 0)
        {
            snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
            pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
            err_sendstring(&msgbuf, tbuf);
        }

        if (edata->internalquery)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
            err_sendstring(&msgbuf, edata->internalquery);
        }

        if (edata->filename)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
            err_sendstring(&msgbuf, edata->filename);
        }

        if (edata->lineno > 0)
        {
            snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
            pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
            err_sendstring(&msgbuf, tbuf);
        }

        if (edata->funcname)
        {
            pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
            err_sendstring(&msgbuf, edata->funcname);
        }

        pq_sendbyte(&msgbuf, '\0'); /* terminator */
    }
    else
    {
        /* Old style --- gin up a backwards-compatible message */
        StringInfoData buf;

        initStringInfo(&buf);

        appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));

        if (edata->show_funcname && edata->funcname)
            appendStringInfo(&buf, "%s: ", edata->funcname);

        if (edata->message)
            appendStringInfoString(&buf, edata->message);
        else
            appendStringInfoString(&buf, _("missing error text"));

        if (edata->cursorpos > 0)
            appendStringInfo(&buf, _(" at character %d"),
                             edata->cursorpos);
        else if (edata->internalpos > 0)
            appendStringInfo(&buf, _(" at character %d"),
                             edata->internalpos);

        appendStringInfoChar(&buf, '\n');

        err_sendstring(&msgbuf, buf.data);

        pfree(buf.data);
    }

    pq_endmessage(&msgbuf);

    /*
     * This flush is normally not necessary, since postgres.c will flush out
     * waiting data when control returns to the main loop. But it seems best
     * to leave it here, so that the client has some clue what happened if the
     * backend dies before getting back to the main loop ... error/notice
     * messages should not be a performance-critical path anyway, so an extra
     * flush won't hurt much ...
     */
    pq_flush();
}


/*
 * Support routines for formatting error messages.
 */


/*
 * expand_fmt_string --- process special format codes in a format string
 *
 * We must replace %m with the appropriate strerror string, since vsnprintf
 * won't know what to do with it.
 *
 * The result is a palloc'd string.
 */
static char *
expand_fmt_string(const char *fmt, ErrorData *edata)
{
    StringInfoData buf;
    const char *cp;

    initStringInfo(&buf);

    for (cp = fmt; *cp; cp++)
    {
        if (cp[0] == '%' && cp[1] != '\0')
        {
            cp++;
            if (*cp == 'm')
            {
                /*
                 * Replace %m by system error string.  If there are any %'s in
                 * the string, we'd better double them so that vsnprintf won't
                 * misinterpret.
                 */
                const char *cp2;

                cp2 = useful_strerror(edata->saved_errno);
                for (; *cp2; cp2++)
                {
                    if (*cp2 == '%')
                        appendStringInfoCharMacro(&buf, '%');
                    appendStringInfoCharMacro(&buf, *cp2);
                }
            }
            else
            {
                /* copy % and next char --- this avoids trouble with %%m */
                appendStringInfoCharMacro(&buf, '%');
                appendStringInfoCharMacro(&buf, *cp);
            }
        }
        else
            appendStringInfoCharMacro(&buf, *cp);
    }

    return buf.data;
}


/*
 * A slightly cleaned-up version of strerror()
 */
static const char *
useful_strerror(int errnum)
{
    /* this buffer is only used if strerror() and get_errno_symbol() fail */
    static char errorstr_buf[48];
    const char *str;

#ifdef WIN32
    /* Winsock error code range, per WinError.h */
    if (errnum >= 10000 && errnum <= 11999)
        return pgwin32_socket_strerror(errnum);
#endif
    str = strerror(errnum);

    /*
     * Some strerror()s return an empty string for out-of-range errno.  This
     * is ANSI C spec compliant, but not exactly useful.  Also, we may get
     * back strings of question marks if libc cannot transcode the message to
     * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
     * get_errno_symbol(), and if that fails, print the numeric errno.
     */
    if (str == NULL || *str == '\0' || *str == '?')
        str = get_errno_symbol(errnum);

    if (str == NULL)
    {
        snprintf(errorstr_buf, sizeof(errorstr_buf),
        /*------
          translator: This string will be truncated at 47
          characters expanded. */
                 _("operating system error %d"), errnum);
        str = errorstr_buf;
    }

    return str;
}

/*
 * Returns a symbol (e.g. "ENOENT") for an errno code.
 * Returns NULL if the code is unrecognized.
 */
static const char *
get_errno_symbol(int errnum)
{// #lizard forgives
    switch (errnum)
    {
        case E2BIG:
            return "E2BIG";
        case EACCES:
            return "EACCES";
#ifdef EADDRINUSE
        case EADDRINUSE:
            return "EADDRINUSE";
#endif
#ifdef EADDRNOTAVAIL
        case EADDRNOTAVAIL:
            return "EADDRNOTAVAIL";
#endif
        case EAFNOSUPPORT:
            return "EAFNOSUPPORT";
#ifdef EAGAIN
        case EAGAIN:
            return "EAGAIN";
#endif
#ifdef EALREADY
        case EALREADY:
            return "EALREADY";
#endif
        case EBADF:
            return "EBADF";
#ifdef EBADMSG
        case EBADMSG:
            return "EBADMSG";
#endif
        case EBUSY:
            return "EBUSY";
        case ECHILD:
            return "ECHILD";
#ifdef ECONNABORTED
        case ECONNABORTED:
            return "ECONNABORTED";
#endif
        case ECONNREFUSED:
            return "ECONNREFUSED";
#ifdef ECONNRESET
        case ECONNRESET:
            return "ECONNRESET";
#endif
        case EDEADLK:
            return "EDEADLK";
        case EDOM:
            return "EDOM";
        case EEXIST:
            return "EEXIST";
        case EFAULT:
            return "EFAULT";
        case EFBIG:
            return "EFBIG";
#ifdef EHOSTUNREACH
        case EHOSTUNREACH:
            return "EHOSTUNREACH";
#endif
        case EIDRM:
            return "EIDRM";
        case EINPROGRESS:
            return "EINPROGRESS";
        case EINTR:
            return "EINTR";
        case EINVAL:
            return "EINVAL";
        case EIO:
            return "EIO";
#ifdef EISCONN
        case EISCONN:
            return "EISCONN";
#endif
        case EISDIR:
            return "EISDIR";
#ifdef ELOOP
        case ELOOP:
            return "ELOOP";
#endif
        case EMFILE:
            return "EMFILE";
        case EMLINK:
            return "EMLINK";
        case EMSGSIZE:
            return "EMSGSIZE";
        case ENAMETOOLONG:
            return "ENAMETOOLONG";
        case ENFILE:
            return "ENFILE";
        case ENOBUFS:
            return "ENOBUFS";
        case ENODEV:
            return "ENODEV";
        case ENOENT:
            return "ENOENT";
        case ENOEXEC:
            return "ENOEXEC";
        case ENOMEM:
            return "ENOMEM";
        case ENOSPC:
            return "ENOSPC";
        case ENOSYS:
            return "ENOSYS";
#ifdef ENOTCONN
        case ENOTCONN:
            return "ENOTCONN";
#endif
        case ENOTDIR:
            return "ENOTDIR";
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
        case ENOTEMPTY:
            return "ENOTEMPTY";
#endif
#ifdef ENOTSOCK
        case ENOTSOCK:
            return "ENOTSOCK";
#endif
#ifdef ENOTSUP
        case ENOTSUP:
            return "ENOTSUP";
#endif
        case ENOTTY:
            return "ENOTTY";
        case ENXIO:
            return "ENXIO";
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
        case EOPNOTSUPP:
            return "EOPNOTSUPP";
#endif
#ifdef EOVERFLOW
        case EOVERFLOW:
            return "EOVERFLOW";
#endif
        case EPERM:
            return "EPERM";
        case EPIPE:
            return "EPIPE";
        case EPROTONOSUPPORT:
            return "EPROTONOSUPPORT";
        case ERANGE:
            return "ERANGE";
#ifdef EROFS
        case EROFS:
            return "EROFS";
#endif
        case ESRCH:
            return "ESRCH";
#ifdef ETIMEDOUT
        case ETIMEDOUT:
            return "ETIMEDOUT";
#endif
#ifdef ETXTBSY
        case ETXTBSY:
            return "ETXTBSY";
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
        case EWOULDBLOCK:
            return "EWOULDBLOCK";
#endif
        case EXDEV:
            return "EXDEV";
    }

    return NULL;
}


/*
 * error_severity --- get string representing elevel
 *
 * The string is not localized here, but we mark the strings for translation
 * so that callers can invoke _() on the result.
 */
static const char *
error_severity(int elevel)
{// #lizard forgives
    const char *prefix;

    switch (elevel)
    {
        case DEBUG1:
        case DEBUG2:
        case DEBUG3:
        case DEBUG4:
        case DEBUG5:
            prefix = gettext_noop("DEBUG");
            break;
        case LOG:
        case LOG_SERVER_ONLY:
            prefix = gettext_noop("LOG");
            break;
        case INFO:
            prefix = gettext_noop("INFO");
            break;
        case NOTICE:
            prefix = gettext_noop("NOTICE");
            break;
        case WARNING:
            prefix = gettext_noop("WARNING");
            break;
        case ERROR:
            prefix = gettext_noop("ERROR");
            break;
        case FATAL:
            prefix = gettext_noop("FATAL");
            break;
        case PANIC:
            prefix = gettext_noop("PANIC");
            break;
        case STOP:
            prefix = gettext_noop("STOP");
            break;
        default:
            prefix = "???";
            break;
    }

    return prefix;
}


/*
 *    append_with_tabs
 *
 *    Append the string to the StringInfo buffer, inserting a tab after any
 *    newline.
 */
static void
append_with_tabs(StringInfo buf, const char *str)
{
    char        ch;

    while ((ch = *str++) != '\0')
    {
        appendStringInfoCharMacro(buf, ch);
        if (ch == '\n')
            appendStringInfoCharMacro(buf, '\t');
    }
}


/*
 * Write errors to stderr (or by equal means when stderr is
 * not available). Used before ereport/elog can be used
 * safely (memory context, GUC load etc)
 */
void
write_stderr(const char *fmt,...)
{
    va_list        ap;

#ifdef WIN32
    char        errbuf[2048];    /* Arbitrary size? */
#endif

    fmt = _(fmt);

    va_start(ap, fmt);
#ifndef WIN32
    /* On Unix, we just fprintf to stderr */
    vfprintf(stderr, fmt, ap);
    fflush(stderr);
#else
    vsnprintf(errbuf, sizeof(errbuf), fmt, ap);

    /*
     * On Win32, we print to stderr if running on a console, or write to
     * eventlog if running as a service
     */
    if (pgwin32_is_service())    /* Running as a service */
    {
        write_eventlog(ERROR, errbuf, strlen(errbuf));
    }
    else
    {
        /* Not running as service, write to stderr */
        write_console(errbuf, strlen(errbuf));
        fflush(stderr);
    }
#endif
    va_end(ap);
}

#ifdef USE_MODULE_MSGIDS
static int
get_overridden_log_level(int moduleid, int fileid, int msgid, int origlevel)
{// #lizard forgives
    uint32 position;
    int value, relative, change, elevel;

    /*
     * The shared memory may not set during init processing or in a stand alone
     * backend.
     */
    if (!IsPostmasterEnvironment || IsInitProcessingMode())
        return origlevel;

    if (!MsgModuleCtl->mm_enabled)
        return origlevel;

    /*
     * Reject invalid bounds
     */
    if ((moduleid <= 0 || moduleid >= PGXL_MSG_MAX_MODULES) ||
        (fileid <= 0 || fileid >= PGXL_MSG_MAX_FILEIDS_PER_MODULE) ||
        (msgid <= 0 || msgid >= PGXL_MSG_MAX_MSGIDS_PER_FILE))
        return origlevel;

    if (origlevel < DEBUG5 || origlevel >= LOG)
        return origlevel;

    if (!(MsgModuleCtl->mm_flags[StartOfBackendFlags + MyBackendId]))
        return origlevel;

    elevel = origlevel;

    /*
     * Get the overridden log level and return it back
     */
    position = (moduleid - 1) * PGXL_MSG_MAX_FILEIDS_PER_MODULE *
            PGXL_MSG_MAX_MSGIDS_PER_FILE +
            (fileid - 1) * PGXL_MSG_MAX_MSGIDS_PER_FILE +
            (msgid - 1);
    
    /* Read once */
    value = MsgModuleCtl->mm_flags[position];

    relative = value & 0x80;
    change = value & 0x7f;

    if (value)
    {
        if (relative)
        {
            elevel = elevel + change;
            if (elevel < DEBUG5)
                elevel = DEBUG5;
            else if (elevel >= LOG)
                elevel = LOG;
        }
        else
            elevel = change;
        return elevel;
    }
    return origlevel;
}
#endif

/*
 * is_log_level_output -- is elevel logically >= log_min_level?
 *
 * We use this for tests that should consider LOG to sort out-of-order,
 * between ERROR and FATAL.  Generally this is the right thing for testing
 * whether a message should go to the postmaster log, whereas a simple >=
 * test is correct for testing whether the message should go to the client.
 */
static bool
is_log_level_output(int elevel,
#ifdef USE_MODULE_MSGIDS
        int moduleid,
        int fileid,
        int msgid,
#endif
        int log_min_level)
{// #lizard forgives
#ifdef USE_MODULE_MSGIDS
    /* 
     * Check if the message's compile time value has been changed during the
     * run time.
     *
     * Currently, we only support increasing the log level of messages and that
     * too only for deciding whether the message should go to the server log or
     * not. A message which would otherwise not qualify to go to the server
     * log, thus can be forced to be logged. 
     *
     * In future, we may also want to go otherway round i.e. supressing a log
     * message or also change severity of log messages. The latter may
     * especially be useful to turn some specific ERROR messages into FATAL or
     * PANIC to be able to get a core dump for analysis.
     */
    elevel = get_overridden_log_level(moduleid, fileid, msgid,
            elevel);
#endif

    if (elevel == LOG || elevel == LOG_SERVER_ONLY)
    {
        if (log_min_level == LOG || log_min_level <= ERROR)
            return true;
    }
    else if (log_min_level == LOG)
    {
        /* elevel != LOG */
        if (elevel >= FATAL)
            return true;
    }
    /* Neither is LOG */
    else if (elevel >= log_min_level)
        return true;

    return false;
}

/*
 * Adjust the level of a recovery-related message per trace_recovery_messages.
 *
 * The argument is the default log level of the message, eg, DEBUG2.  (This
 * should only be applied to DEBUGn log messages, otherwise it's a no-op.)
 * If the level is >= trace_recovery_messages, we return LOG, causing the
 * message to be logged unconditionally (for most settings of
 * log_min_messages).  Otherwise, we return the argument unchanged.
 * The message will then be shown based on the setting of log_min_messages.
 *
 * Intention is to keep this for at least the whole of the 9.0 production
 * release, so we can more easily diagnose production problems in the field.
 * It should go away eventually, though, because it's an ugly and
 * hard-to-explain kluge.
 */
int
trace_recovery(int trace_level)
{
    if (trace_level < LOG &&
        trace_level >= trace_recovery_messages)
        return LOG;

    return trace_level;
}

#ifdef USE_MODULE_MSGIDS
Size
MsgModuleShmemSize(void)
{
    /*
     * One byte per message to store overridden log level.
     * !!TODO We don't really need a byte and a few bits would be enough. So
     * look for improving this/
     *
     * What we have done is a very simplisitic representation of msg-ids. The
     * overall memory requirement of this representation is too large as
     * compared to the actual number of msgs. For example, both
     * PGXL_MSG_MAX_MSGIDS_PER_FILE and PGXL_MSG_MAX_FILEIDS_PER_MODULE are set
     * to the largest value that any one module uses, but the actual values are
     * much smaller
     *
     * Also have a separate area for MaxBackends so that caller can selectively
     * change logging for individual backends. Note that we don't support
     * having different logging levels for different backends. So either a
     * backend honours the module/file/msg level overrides or it does not.
     */
    return SizeOfMsgModuleCtlStruct;
}

void
MsgModuleShmemInit(void)
{
    bool found;

    MsgModuleCtl = ShmemInitStruct("Message Module Struct",
                                   SizeOfMsgModuleCtlStruct,
                                   &found);
}

void
AtProcStart_MsgModule(void)
{
    if (MsgModuleCtl->mm_persistent)
        MsgModuleCtl->mm_flags[StartOfBackendFlags + MyBackendId] = true;
    else
        MsgModuleCtl->mm_flags[StartOfBackendFlags + MyBackendId] = false;
    before_shmem_exit(AtProcExit_MsgModule, 0);
}

static bool
pg_msgmodule_internal(int32 moduleid, int32 fileid, int32 msgid, char value)
{// #lizard forgives
    uint32 start_position;
    uint32 len;

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));


    if (moduleid <= 0 || moduleid >= PGXL_MSG_MAX_MODULES)
        ereport(ERROR, (errmsg_internal("Invalid module id %d, allowed values 1-%d",
                        moduleid, PGXL_MSG_MAX_MODULES)));

    if (fileid == -1)
    {
        /*
         * All messages in the given module to be overridden with the given
         * level
         */
        len = PGXL_MSG_MAX_FILEIDS_PER_MODULE * PGXL_MSG_MAX_MSGIDS_PER_FILE;
        start_position = (moduleid - 1) * len;
        memset(MsgModuleCtl->mm_flags + start_position, value, len);
        goto success;
    }
    else
    {
        if (fileid <= 0 || fileid >= PGXL_MSG_MAX_FILEIDS_PER_MODULE)
            ereport(ERROR, (errmsg_internal("Invalid file id %d, allowed values 1-%d",
                            fileid, PGXL_MSG_MAX_FILEIDS_PER_MODULE)));
        
        /*
         * All messages in the given <module, file> to be overridden with the
         * given level
         */
        if (msgid == -1)
        {
            len = PGXL_MSG_MAX_MSGIDS_PER_FILE;
            start_position = ((moduleid - 1) * PGXL_MSG_MAX_FILEIDS_PER_MODULE * PGXL_MSG_MAX_MSGIDS_PER_FILE) +
                        (fileid - 1) * PGXL_MSG_MAX_MSGIDS_PER_FILE;
            memset(MsgModuleCtl->mm_flags + start_position, value, len);
            goto success;
        }

        if (msgid <= 0 || msgid >= PGXL_MSG_MAX_MSGIDS_PER_FILE)
            ereport(ERROR, (errmsg_internal("Invalid msg id %d, allowed values 1-%d",
                            fileid, PGXL_MSG_MAX_MSGIDS_PER_FILE)));

        /*
         * Deal with a specific <module, file, msg>
         */
        len = sizeof (char);
        start_position = ((moduleid - 1) * PGXL_MSG_MAX_FILEIDS_PER_MODULE * PGXL_MSG_MAX_MSGIDS_PER_FILE) +
            ((fileid - 1) * PGXL_MSG_MAX_MSGIDS_PER_FILE) +
            (msgid - 1);
        memset(MsgModuleCtl->mm_flags + start_position, value, len);
        goto success;
    }

success:
    return true;
}

Datum
pg_msgmodule_set(PG_FUNCTION_ARGS)
{// #lizard forgives
    int32 moduleid = PG_GETARG_INT32(0);
    int32 fileid = PG_GETARG_INT32(1);
    int32 msgid = PG_GETARG_INT32(2);
    const char *levelstr = text_to_cstring(PG_GETARG_TEXT_PP(3));
    int32 level;

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));

    /*
     * The only accepted values for the log levels are - LOG, DEBUG[1-5] and
     * DEFAULT
     */
    if (strcasecmp(levelstr, "LOG") == 0)
        level = LOG;
    else if (strcasecmp(levelstr, "DEFAULT") == 0)
        level = 0;
    else if (strcasecmp(levelstr, "DEBUG1") == 0)
        level = DEBUG1;
    else if (strcasecmp(levelstr, "DEBUG2") == 0)
        level = DEBUG2;
    else if (strcasecmp(levelstr, "DEBUG3") == 0)
        level = DEBUG3;
    else if (strcasecmp(levelstr, "DEBUG4") == 0)
        level = DEBUG4;
    else if (strcasecmp(levelstr, "DEBUG5") == 0)
        level = DEBUG5;
    else
        ereport(ERROR,
                (errcode(ERRCODE_INTERNAL_ERROR),
                 (errmsg("Invalid value \"%s\" for log level", levelstr))));

    PG_RETURN_BOOL(pg_msgmodule_internal(moduleid, fileid, msgid, level));
}

Datum
pg_msgmodule_change(PG_FUNCTION_ARGS)
{
    int32 moduleid = PG_GETARG_INT32(0);
    int32 fileid = PG_GETARG_INT32(1);
    int32 msgid = PG_GETARG_INT32(2);
    int32 change = PG_GETARG_INT32(3);
    int level;

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));
    
    if ((change < (DEBUG5 - LOG)) || (change > (LOG - DEBUG5)))
        ereport(ERROR,
                (errcode(ERRCODE_INTERNAL_ERROR),
             (errmsg("accepted values are between %d and +%d", DEBUG5 - LOG, LOG -
                     DEBUG5))));

    level = 0x80 | change;

    PG_RETURN_BOOL(pg_msgmodule_internal(moduleid, fileid, msgid, level));
}

static bool
pg_msgmodule_enable_disable(int32 pid, bool enable)
{// #lizard forgives
    BackendId backendId;
    /*
     * pid == -1 implies the action is applied for all backends
     */
    if (pid != -1)
    {
        volatile PGPROC *proc = BackendPidGetProc(pid);
        if (proc == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_INTERNAL_ERROR),
                     (errmsg("PID %d is not a PostgreSQL server process", pid))));
        backendId = proc->backendId;
        MsgModuleCtl->mm_flags[StartOfBackendFlags + backendId] = enable ? true :
            false;
    }
    else
    {
        /*
         * All backends should not honour the current settings
         */
        memset(MsgModuleCtl->mm_flags + StartOfBackendFlags, true,
                MaxBackends);
    }

    /*
     * If we are disabling the last backend and no new backends are going to
     * participate in the facility, then just disable the entire facility so
     * that subsequent backends can quickly bail out
     *
     * XXX There is a race possible here if another call to turn on the logging
     * comes just after we had passed over tje slot. We should possibly protect
     * access to the shared memory, but doesn't seem like worth the effort
     * right now
     */
    if (!enable && !MsgModuleCtl->mm_persistent)
    {
        int i;
        for (i = 0; i < MaxBackends; i++)
            if (MsgModuleCtl->mm_flags[StartOfBackendFlags + i])
                break;
        if (i == MaxBackends)
            MsgModuleCtl->mm_enabled = false;
    }
    else
        MsgModuleCtl->mm_enabled = true;

    return true;
}

Datum
pg_msgmodule_enable(PG_FUNCTION_ARGS)
{
    int32 pid = PG_GETARG_INT32(0);

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));

    PG_RETURN_BOOL(pg_msgmodule_enable_disable(pid, true));
}

Datum
pg_msgmodule_disable(PG_FUNCTION_ARGS)
{
    int32 pid = PG_GETARG_INT32(0);

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));

    PG_RETURN_BOOL(pg_msgmodule_enable_disable(pid, false));
}

/*
 * pg_msgmodule_enable_all(bool persistent)
 *
 * All processes to start honouring the current settings of overridden log
 * levels. If "persistent" is set to true, then all future processes will also
 * honour the settings.
 */
Datum
pg_msgmodule_enable_all(PG_FUNCTION_ARGS)
{
    bool persistent = PG_GETARG_BOOL(0);

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));

    MsgModuleCtl->mm_persistent = persistent;
    if (persistent)
    {
        MsgModuleCtl->mm_enabled = true;
        memset(MsgModuleCtl->mm_flags + StartOfBackendFlags, true,
                MaxBackends);
        PG_RETURN_BOOL(true);
    }
    else
        PG_RETURN_BOOL(pg_msgmodule_enable_disable(-1, true));
}

/*
 * Disable all current and future processes from honouring the overridden log
 * levels
 */
Datum
pg_msgmodule_disable_all(PG_FUNCTION_ARGS)
{
    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             (errmsg("must be superuser to change elog message level"))));

    MsgModuleCtl->mm_enabled = false;
    MsgModuleCtl->mm_persistent = false;
    memset(MsgModuleCtl->mm_flags + StartOfBackendFlags, false,
            MaxBackends);
    PG_RETURN_BOOL(true);
}

/*
 * Handle proc exit. If persistent flag is not set then also check if we are
 * the last process using the facility and if so, disable it. The current log
 * settings are retained and will be used when newer processes are enabled for
 * the facility
 */
static void
AtProcExit_MsgModule(int code, Datum arg)
{
    MsgModuleCtl->mm_flags[StartOfBackendFlags + MyBackendId] = false;
    if (!MsgModuleCtl->mm_persistent)
    {
        int i;
        for (i = 0; i < MaxBackends; i++)
            if (MsgModuleCtl->mm_flags[StartOfBackendFlags + i])
                break;
        if (i == MaxBackends)
            MsgModuleCtl->mm_enabled = false;
    }
}
#else
Datum
pg_msgmodule_set(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
Datum
pg_msgmodule_change(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
Datum
pg_msgmodule_enable(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
Datum
pg_msgmodule_disable(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
Datum
pg_msgmodule_enable_all(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
Datum
pg_msgmodule_disable_all(PG_FUNCTION_ARGS)
{
    ereport(ERROR, (errmsg_internal("Module msgid support not available. "
                    "Please recompile with --enable-genmsgids")));
}
#endif
#ifdef __OPENTENBASE__
/* get error message in error stack */
bool
GetErrorMsg(StringInfo buf)
{
    if (errordata_stack_depth < 0)
    {
        /* no error message */
        return false;
    }
    else
    {
        ErrorData  *edata = &errordata[errordata_stack_depth];

        /* errmessage */
        if (edata->message)
        {
            appendCSVLiteral(buf, edata->message);
            appendStringInfoChar(buf, ',');
        }

        /* errdetail or errdetail_log */
        if (edata->detail_log)
        {
            appendCSVLiteral(buf, edata->detail_log);
        }
        else if (edata->detail)
        {
            appendCSVLiteral(buf, edata->detail);
        }
        appendStringInfoChar(buf, ',');

        /* errhint */
        if (edata->hint)
        {
            appendCSVLiteral(buf, edata->hint);
            appendStringInfoChar(buf, '.');
        }

        return true;
    }
}

#endif
